2

I have a Python 3.5 script that I would like to invoke as a pre-build step in my Code Composer build. To be clear, it should be run as one of the entries in (my project) > Properties > CCS Build > Steps > Pre-build steps.

The script currently begins with the hashbang #!/usr/bin/env python3, but I can change this.

On Linux, I can invoke the script as ../prebuild.py ../output_file. This fails on Windows 10 with:

"C:\\ti\\ccsv6\\utils\\bin\\gmake" -k all 
../prebuild.py ../output_file
makefile:217: recipe for target 'pre-build' failed
process_begin: CreateProcess(NULL, env python3 C:\path\to\prebuild.py ../output_file, ...) failed.
make (e=2): The system cannot find the file specified.

The path separator does not affect this at all.

I also tried python3 ../prebuild.py ../output_file. This does not work on Windows 10 because there is no python3 executable. Python 3 is installed as python.exe. Using python fails on Linux because of course Python 3 is installed as python3, and python refers to Python 2.

I also tried py ../prebuild.py ../output_file. This fails on Linux because there is no py executable.

Is there a cross-platform way to invoke a Python 3 script that can be used for an Eclipse pre-build step? I would like to avoid requiring that developers modify their distribution/Python installation.

I am using Code Composer Studio 6, which is based on Eclipse. I expect any answer to this would apply to either.

Context

One of the things I am trying to achieve is to insert the SHA1 of the current Git commit into a file. The accepted answer for doing this is to generate the file as part of the build process by parsing Git output. I have a Python script that can do this on both Windows and Linux, so I need a way to invoke it as part of Eclipse's build process.

Community
  • 1
  • 1
detly
  • 29,332
  • 18
  • 93
  • 152
  • I am not above writing a weird polyglot line, which can be documented, if I can make the development workflow easier in the long run. – detly Aug 17 '16 at 03:32
  • Is the [Launcher for Windows](https://docs.python.org/3/using/windows.html#python-launcher-for-windows) any use for you here? – Peter Brittain Aug 17 '16 at 09:29
  • @PeterBrittain Nope, it doesn't exist on Linux, so the build step will just fail there. I realise we could symlink `py -> python3` on Linux, but my preference is for a complex build step that just works over requiring installation or symlinking stuff on the development machine (because the build step is part of the project, which is just checked in to version control, whereas dependencies can't be). – detly Aug 17 '16 at 11:05
  • Obviously a simple build step is better than either, but hey, we can't have everything `:)` – detly Aug 17 '16 at 11:11
  • Sorry - I wasnt clear... I meant can you associate the `.py` extension with the launcher on Windows and then use the same shebang in both environments? – Peter Brittain Aug 17 '16 at 14:30
  • @PeterBrittain It's not a bad idea, but it does mean double-click-to-edit won't work any more for Python scripts on windows, which might be pretty inconvenient for a dev machine. But so far I can't see any other way to do it. I'll try it and see if Eclipse/CCS does the right thing. – detly Aug 17 '16 at 23:24
  • Eh, doesn't work anyway. Still get `process_begin: CreateProcess(NULL, env python3 C:\path\to\prebuild.py ../output_file, ...) failed. make (e=2): The system cannot find the file specified.` – detly Aug 17 '16 at 23:30
  • My guess is that the launcher can't find python3... [Enabling diags](https://docs.python.org/3/using/windows.html#diagnostics) should prove this. – Peter Brittain Aug 18 '16 at 07:12
  • @PeterBrittain I can run `py prebuild.py output_file` from the command line though, and it works fine. I don't think Eclipse is even invoking `py` unless I put it on the pre-build step command line. As in, if I just have a bare `../prebuild.py ../output_file`, it won't look up the Windows association for `.py` files and run that, it'll just try to process the hashbang line itself. – detly Aug 18 '16 at 07:18
  • I should point out that I don't really know how Eclipse/CCS deals with its pre-build steps, so I can't answer much about what it's doing under the hood. I can't figure out if it's parsing the hashbang itself, implementing it's own shell or using `cmd`, etc. So I can try these things, but I might not be able to answer any questions about *why*. – detly Aug 18 '16 at 07:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/121241/discussion-between-peter-brittain-and-detly). – Peter Brittain Aug 18 '16 at 11:03
  • You've probably tried absolute paths and properly escaping backslashes on Windows paths (`\\`)? – handle Aug 20 '16 at 16:03
  • @handle Absolute paths won't work, the project and script could be in any directory on different platforms (unless I try to use Eclipse's env vars, perhaps?). But how would I use native path separators on both Windows and Linux? – detly Aug 20 '16 at 23:24
  • @PeterBrittain I've added an example of something I'm trying to do with this. Ultimately I would like to do more, but this is a good starting point. – detly Aug 21 '16 at 02:12
  • Also ping @handle. – detly Aug 21 '16 at 02:12
  • From your (Windows) error messages it looks like `make` is trying to run `env python3`? Does this change with the hashbang? Maybe you can change this or find/write your own starter program that invokes python and passes the remaining arguments (maybe use "cmd", "start"?). – handle Aug 21 '16 at 08:27
  • @handle that is exactly what is happening, which is why I already asked about the Launcher for Windows. Detly, what happened when you tried decoupling via a shell script/batch job? – Peter Brittain Aug 21 '16 at 09:00
  • @PeterBrittain That does not help if gmake or Eclipse interprets the python shebang itself. Doesn't Eclipse generate the Makefile(s) before running make? Maybe detly can show how the pre-build steps look in there. In my previous comment, I meant that providing the windows equivalent of `env` might help. Maybe just rename py.exe to env.exe (No, doesn't work: `G:\>where py.exe --> C:\Windows\py.exe G:\>py python3 --> C:\Program Files\Python35\python.exe: can't open file 'python3': [Errno 2] No such file or directory`). But a batch file to map this might? – handle Aug 21 '16 at 09:21
  • Not sure if Windows might call executables with space in the filename: "py.exe" -> "env python3.exe" might be worth a try. – handle Aug 21 '16 at 09:48
  • @PeterBrittain Thanks for the suggestions, even though it was another answer that got me going in the right direction! – detly Aug 23 '16 at 00:10
  • As an aside, it boggles my mind a bit that I am using a cross platform IDE, a cross platform compiler, a cross platform language for my build scripts, and a cross platform VCS, and I *still* have to write a hacky reimplementation of `py` to do something. `` – detly Aug 23 '16 at 00:14
  • 1
    Somewhere between CCStudio 6.1.2 and 6.2.0 they switched from GNU make 3.81 to 4.1. It seems that this introduced a feature that attempts to interpret the shebang rather than just passing the file on to Windows to handle via the .py->py.exe assocation. http://git.savannah.gnu.org/cgit/make.git/tree/w32/subproc/sub_proc.c?id=9d58570c77240fed53d1f88217877f8e778f4bb2#n621 – altendky Jan 27 '17 at 18:21

2 Answers2

2

Have a wrapper script that works under both Python 2 and 3 to detect and run the script with the correct Python version. The Eclipse/CCS pre build step can then be python ../wrapper.py (possibly with extra arguments like ../prebuild.py args).

You could just check if it is running on Windows or Linux and what version of Python it is running. If it is running on Linux and running the wrong Python version, run subprocess.call(['python3','prebuild.py']). To check the Python version and OS use:

import os, sys, subprocess

if sys.version_info[0] < 3 and os.name == 'posix':
     subprocess.call(['python3','../prebuild.py'])
     sys.exit()
else:
    subprocess.call(['python','../prebuild.py'])
    sys.exit()

A more generic script might check if the interpreter is already the right one and try to pass through arguments if it is:

import sys

if sys.version_info[0] >= 3 and sys.version_info[1] >= 3:
    subprocess.call([sys.executable] + sys.argv[1:]

Otherwise the wrapper could iterate over a list of possible interpreters until it succeeds, like:

interpreters = [["py", "-3"], ["python3"]]

for interp in interpreters:
     # Try a subprocess.call(...) as above. Detect bad return codes (eg. py failed) or OSErrors (ie. command wasn't found).
     success = False
     try:
        success = subprocess.call(...) == 0
     except OSError:
         pass

     if success:
         break
Community
  • 1
  • 1
Greensheep
  • 111
  • 2
  • 5
  • How does the detection script get run? – detly Aug 21 '16 at 22:01
  • The detection script would be in the same folder or in the example I gave in a sub folder where the prebuild.py script would be. Then you would just run it as python detection.py which would run the prebuild.py with the correct version of python. Again it seems hacky but that is what first came to mind. That was just for the detection you also most likely want to pass the output_file but I left that part out just to focus on the switch to the correct python version. – Greensheep Aug 21 '16 at 22:06
  • Ah, and the command I put into Eclipse's build step is `python ../switch_prebuild.py` I suppose. Implies a dependency on Python 2 in Linux, but I think most distributions rely on that now. – detly Aug 21 '16 at 22:12
  • It implies a version python 2 or greater is installed. Because some version of linux has 'python' default to 3.5 others have it default to 2.7. But most will have something that points to something called python. So if only python 3 is installed the script will still work and call the correct version of python. Also this script works in both python 2.7 and 3.5 without any issue that I have see. Again you still need to add something to pass in the output_file path/name. – Greensheep Aug 21 '16 at 22:41
  • Your answer got me on the right path, but my solution ended up looking quite different. Do you mind if I make a substantial edit to this to show what I did? – detly Aug 22 '16 at 23:01
  • What about `py.exe`? The existing tool designed to handle this? `.py` files should get associated with it on installation of python and it respects the shebangs. Though admittedly it seems that some version of Eclipse in the past year broke this. – altendky Jan 17 '17 at 21:10
  • @altendky I mention in the question — `py` is only installed on Windows. It does not exist on Linux. – detly Jan 27 '17 at 18:25
  • @detly Yes, when I left that comment my experience with `#!/usr/bin/env python3` and ccstudio was that they just worked. I didn't mean you should use `py.exe` in Linux where it doesn't exist. See my explanation of when this was broken in my comment on your original question. – altendky Jan 27 '17 at 19:33
  • @altendky ah I get your meaning now. I find the whole situation really bizarre on several levels. – detly Jan 27 '17 at 19:36
  • @detly i was going to make my own `env.py` to compensate without having to change the script files or the project... but they don't even use `$PATHEXT` and instead hardcode it so I'm stuck solving this with `env.bat`. *shudder* http://git.savannah.gnu.org/cgit/make.git/tree/w32/subproc/sub_proc.c?id=9d58570c77240fed53d1f88217877f8e778f4bb2#n506 – altendky Jan 27 '17 at 19:44
0

Somewhere between CCStudio 6.1.2 and 6.2.0 they switched from GNU make 3.81 to 4.1. It seems that this introduced a feature that attempts to interpret the shebang rather than just passing the file on to Windows to handle via the .py->py.exe assocation.

I didn't want to update various projects and branches just because GNU make was being naughty so I decided to solve this in a little different direction by creating env for Windows. Well, a minimal version of it that works for my present need (python3). Since GNU make doesn't bother to us the $PATHEXT variable when searching for matches I couldn't use env.py and beyond that it doesn't even seem to successfully search it's own specified list for anything other than .exe. So, C++ it is. If I need to do much more I will likely make env.exe just call py env.py and do the rest in Python.

Here's a copy of my simple first pass from my gist.

#include <iostream>
#include <string>

using namespace std;

int main (int argc, char* argv[])
{
    string command = string("py");

    cout << " - - - - - - - \\/  \\/ - - - - - " << std::endl;
    for (int i = 2; i < argc; i++)
    {
        cout << argv[i] << endl;
        command += " ";
        command += '"';
        command += argv[i];
        command += '"';
    }
    cout << " - - - - - - - /\\  /\\ - - - - - " << std::endl;
    cout << command << endl;
    cout << " - - - - - - - /\\  /\\ - - - - - " << std::endl;

    system( command.c_str() );

    return 0;
}
altendky
  • 4,176
  • 4
  • 29
  • 39