pythonxbuild

Python subprocess.popen fails when interacting with the subprocess


I have a python build script for a Xamarin application that I need to compile into different ipa's and apk's based on locale.

The script manipulates the necessary values in info.plist and the Android manifest and then builds each of the versions using subprocess.popen to call xbuild. Or at least that's how it's suppose to be.

The problem is that when I in anyway interact with the subprocess (basically i need to wait until it's done before I start changing values for the next version)

This works:

    build_path = os.path.dirname(os.path.realpath(__file__))
    ipa_path = "/path/to/my.ipa"

    cmd = '/Library/Frameworks/Mono.framework/Versions/4.6.2/Commands/xbuild /p:Configuration="Release" /p:Platform="iPhone" /p:IpaPackageDir="%s" /t:Build %s/MyApp/iOS/MyApp.iOS.csproj' % (ipa_path, build_path)

    subprocess.Popen(cmd, env=os.environ, shell=True)

However it will result in the python script continuing in parallel with the build.

If I do this:

    subprocess.Popen(cmd, env=os.environ, shell=True).wait()

Xbuild fail with the following error message:

    Build FAILED.
    Errors:

    /Users/sune/dev/MyApp/iOS/MyApp.iOS.csproj: error :
    /Users/sune/dev/MyApp/iOS/MyApp.iOS.csproj: There is an unclosed literal string.
    Line 2434, position 56.

It fails within milliseconds of being called, whereas normally the build process takes several minutes

Any other shorthand methods of subprocess.popen such as .call, .check_call, and the underlying operations of subprocess.poll and subprocess.communicate causes the same error to happen.

What's really strange is that even calling time.sleep can provoke the same error:

    subprocess.Popen(cmd, env=os.environ, shell=True)

    time.sleep(2)

Which I don't get because as I understand it I should also be able to do something like this:

    shell = subprocess.Popen(cmd, env=os.environ, shell=True)

    while shell.poll() is None:
        time.sleep(2)

    print "done"

To essentially achieve the same as calling shell.wait()

Edit: Using command list instead of string

If I use a command list and shell=False like this

      cmd = [
        '/Library/Frameworks/Mono.framework/Versions/4.6.2/Commands/xbuild',
        '/p:Configuration="Release"',
        '/p:Platform="iPhone"',
        '/p:IpaPackageDir="%s' % ipa_path,
        '/t:Build %s/MyApp/iOS/MyApp.iOS.csproj' % build_path

    ]

    subprocess.Popen(cmd, env=os.environ, shell=False)

Then this is the result:

    MSBUILD: error MSBUILD0003: Please specify the project or solution file to build, as none was found in the current directory.

Any input is much appreciated. I'm banging my head against the wall here.


Solution

  • I firmly believe that this is not possible. It must be a shortcoming of the way the subprocess module is implemented.

    xbuild spawns multiple subprocesses during the build and if polled for status the subprocess in python will discover that one of these had a non-zero return status and stop the execution of one or more of the xbuild subprocesses causing the build to fail as described.

    I ended up using a bash script to do the compiling and use python to manipulate xml files etc.