Distributing .app file after command line xcodebui

2020-06-06 20:06发布

I'm building/archiving my Mac app for distribution from a command line call (below), with Xcode 4.3 installed. To be clear, I didn't have a working solution for this problem earlier to Xcode 4.3, so advice for earlier Xcode releases could easily still be valid. Here's the call:

/usr/bin/xcodebuild -project "ProjectPath/Project.pbxproj" -scheme "Project" -sdk macosx10.7 archive

This runs successfully, and it generates an .xcarchive file, located in my ~/Library/Developer/Xcode/Archives/<date> folder. What's the proper way to get the path the the archive file generated? I'm looking for a way to get a path to the .app file contained therein, so I can distribute it.

I've looked at the MAN page for xcodebuild (and done copious searching online) and didn't find any clues there.

3条回答
别忘想泡老子
2楼-- · 2020-06-06 20:52

There is an easier way, simply specify the archivePath you want to archive:

xcodebuild -archivePath GoTray -scheme GoTray archive

Then you will get the xcarchive file at GoTray.xcarchive in current directory.

Next, run xcodebuild again to export app from the xcarchive file:

xcodebuild -exportArchive -exportFormat APP -archivePath GoTray.xcarchive -exportPath GoTray
查看更多
姐就是有狂的资本
3楼-- · 2020-06-06 21:05

Building on the answer provided here, I came up with a satisfactory multi-part solution. The key to it all, was to use the environment variables Xcode creates during the build.

First, I have a post-action on the Archive phase of my build scheme (pasted into the Xcode project's UI). It calls a Python script I wrote (provided in the next section), passing it the names of the environment variables I want to pull out, and a path to a text file:

# Export the archive paths to be used after Archive finishes
"${PROJECT_DIR}/Script/grab_env_vars.py" "${PROJECT_DIR}/build/archive-env.txt"
"ARCHIVE_PATH" "ARCHIVE_PRODUCTS_PATH" "ARCHIVE_DSYMS_PATH"
"INSTALL_PATH" "WRAPPER_NAME"

That script then writes them to a text file in key = value pairs:

import sys
import os

def main(args):
    if len(args) < 2:
        print('No file path passed in to grab_env_vars')
        return

    if len(args) < 3:
        print('No environment variable names passed in to grab_env_vars')

    output_file = args[1]

    output_path = os.path.dirname(output_file)
    if not os.path.exists(output_path):
        os.makedirs(output_path)

    with open(output_file, 'w') as f:
        for i in range(2, len(args)):
            arg_name = args[i]
            arg_value = os.environ[arg_name]
            #print('env {}: {}'.format(arg_name, arg_value))
            f.write('{} = {}\n'.format(arg_name, arg_value))

def get_archive_vars(path):
    return dict((line.strip().split(' = ') for line in file(path)))

if __name__ == '__main__':
    main(sys.argv)

Then, finally, in my build script (also Python), I parse out those values and can get to the path of the archive, and the app bundle therein:

env_vars = grab_env_vars.get_archive_vars(ENV_FILE)
archive_path = env_vars['ARCHIVE_PRODUCTS_PATH']
install_path = env_vars['INSTALL_PATH'][1:] #Chop off the leading '/' for the join below
wrapper_name = env_vars['WRAPPER_NAME']
archived_app = os.path.join(archive_path, install_path, wrapper_name)

This was the way I solved it, and it should be pretty easily adaptable to other scripting environments. It makes sense with my constraints: I wanted to have as little code as possible in the project, I prefer Python scripting to Bash, and this script is easily reusable in other projects and for other purposes.

查看更多
Juvenile、少年°
4楼-- · 2020-06-06 21:05

You could just use a bit of shell, get the most recent folder in the Archives dir (or use the current date), and then get the most recent archive in that directory.

查看更多
登录 后发表回答