Wygenerowanie xcarchive do określonego folderu z wiersza poleceń

Dla celów CI, muszę być w stanie wygenerować plik XCARCHIVE i IPA w naszej nocnej kompilacji. IPA jest dla naszych testerów, do podpisania za pomocą naszych kluczy ad-hoc, a XCARCHIVE ma wysłać do klienta, aby mógł zaimportować go do Xcode i przesłać do app store, gdy będą z niego zadowoleni.

Generowanie IPA jest dość proste z odrobiną googlowania, jednak jak wygenerować .Umyka mi plik XCARCHIVE. The closest I ' ve found jest:

xcodebuild -scheme myscheme archive

Jednak to przechowuje .xcarchive w jakimś trudnym do znalezienia folderze, np:

/Users/me/Library/Developer/Xcode/Archives/2011-12-14/MyApp 14-12-11 11.42 AM.xcarchive

Czy Jest jakiś sposób, aby kontrolować, gdzie archiwum jest umieszczone, jak się nazywa i jak uniknąć konieczności ponownej kompilacji? Myślę, że najlepszym możliwym wynikiem byłoby wygenerowanie xcarchive z DSYM i aplikacji, które są generowane podczas robienia 'xcodebuild' - czy to możliwe ?

Author: jscs, 2012-01-04

7 answers

Xcode 5 obsługuje teraz opcję -archivePath:

xcodebuild -scheme myscheme archive -archivePath /path/to/AppName.xcarchive

Możesz teraz również wyeksportować podpisany IPA z archiwum, które właśnie zbudowałeś:

xcodebuild -exportArchive -exportFormat IPA -exportProvisioningProfile my_profile_name -archivePath /path/to/AppName.xcarchive -exportPath /path/to/AppName.ipa
 57
Author: rectalogic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-03 14:27:14

Począwszy od Xcode 4 Preview 5 istnieją trzy zmienne środowiskowe, które są dostępne w archiwum scheme po-akcjach.

ARCHIVE_PATH: The path to the archive.
ARCHIVE_PRODUCTS_PATH: The installation location for the archived product.
ARCHIVE_DSYMS_PATH: The path to the product’s dSYM files.
Możesz przenieść/skopiować archiwum tutaj. Chciałem mieć trochę większą kontrolę nad procesem w skrypcie CI, więc zapisałem plik tymczasowy, który można łatwo pozyskać w moim skrypcie CI, który zawierał te wartości.
BUILD_DIR=$PROJECT_DIR/build
echo "ARCHIVE_PATH=\"$ARCHIVE_PATH\"" > $BUILD_DIR/archive_paths.sh
echo "ARCHIVE_PRODUCTS_PATH=\"$ARCHIVE_PRODUCTS_PATH\"" >> $BUILD_DIR/archive_paths.sh
echo "ARCHIVE_DSYMS_PATH=\"$ARCHIVE_DSYMS_PATH\"" >> $BUILD_DIR/archive_paths.sh
echo "INFOPLIST_PATH=\"$INFOPLIST_PATH\"" >> $BUILD_DIR/archive_paths.sh

Następnie w moim skrypcie CI mogę uruchomić następujące:

xcodebuild -alltargets -scheme [Scheme Name] -configuration [Config Name] clean archive
source build/archive_paths.sh
ARCHIVE_NAME=AppName-$APP_VERSION-$APP_BUILD.xcarchive
cp -r "$ARCHIVE_PATH" "$BUILD_DIR/$ARCHIVE_NAME"
 43
Author: Aron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-23 13:50:53

Właśnie rozwiązałem ten - wystarczy dodać argument -archivePath do linii poleceń Xcode build, biorąc pod uwagę początkowe pytanie, które oznaczałoby:

xcodebuild -scheme myscheme archive

Staje się ...

xcodebuild -scheme myscheme archive -archivePath Build/Archive

(uwaga: ścieżki są względne, wyprowadzam mój build do $PWD/Build)

To następnie umieścić swoje .folder aplikacji w:

Build/Archive.xarchive/Products/Application

Jeśli obiekt docelowy kompilacji ma już certyfikat podpisywania i profil aprowizacji, możesz utworzyć plik IPA bez ponownego podpisywania za pomocą następujących narzędzi polecenie:

xcrun -v -sdk iphoneos PackageApplication -v `pwd`'/Build/Archive.xarchive/Products/Application/my.app' -o `pwd`'/myapp.ipa'
Xcrun nie lubi ścieżek względnych, stąd pwd)

Args-v wyrzuca wiele przydatnych informacji - ta komenda może nie podpisać poprawnie i nadal zakończyć się kodem 0, westchnienie!

Jeśli okaże się, że nie można uruchomić zbudowany .ipa jest to prawdopodobnie problem z podpisywaniem, który możesz sprawdzić używając:

codesign --verify -vvvv myapp.app

Jeśli jest poprawnie podpisany i nie modyfikowany, wyjście będzie miało to w:

myapp.app: valid on disk
myapp.app: satisfies its Designated Requirement

Jeśli nie zobaczysz coś podobnego do tego:

Codesign check fails : /blahpath/myapp.app: a sealed resource is missing or invalid
file modified: /blahpath/ls-ios-develop.app/Assets.car

... co ogólnie oznacza, że próbujesz użyć pośredniego katalogu wyjściowego, a nie odpowiedniego archiwum.

 11
Author: Oliver Dungey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-19 15:29:27

Moim obecnym rozwiązaniem jest zmiana nazwy istniejącego folderu archiwów użytkownika, uruchomienie kompilacji I "find", aby skopiować archiwa tam, gdzie chcę, a następnie usunąć folder archiwów i zmienić nazwę starego folderu z powrotem, z kodem takim jak ten w skrypcie ruby build:

# Move the existing archives out of the way
system('mv ~/Library/Developer/Xcode/Archives ~/Library/Developer/Xcode/OldArchivesTemp')
# Build the .app, the .DSYM, and the .xcarchive
system("xcodebuild -scheme \"#{scheme}\" clean build archive CONFIGURATION_BUILD_DIR=\"#{build_destination_folder}\"")
# Find the xcarchive wherever it was placed and copy it where i want it
system("find ~/Library/Developer/Xcode/Archives -name *.xcarchive -exec cp -r {} \"#{build_destination_folder}\" \";\"")
# Delete the new archives folder with this new xcarchive
system('rm -rf ~/Library/Developer/Xcode/Archives')
# Put the old archives back
system('mv ~/Library/Developer/Xcode/OldArchivesTemp ~/Library/Developer/Xcode/Archives')

Its a bit hacky but I don ' t see a better solution currently. Przynajmniej zachowuje folder 'archives' użytkownika i wszystkie jego istniejące archiwa.

--Ważna uwaga!--

Od tego czasu dowiedziałem się, że linia kodu, w której znajduję archiwum i cp go do folderu, który chcę, nie kopiuje dowiązań symbolicznych wewnątrz archiwum poprawnie, co łamie podpisywanie kodu w aplikacji. Będziesz chciał zastąpić to "mv" lub czymś, co utrzymuje dowiązania symboliczne. Zdrowie!

 2
Author: Chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-12 09:45:23

Oto mały bash, który wymyśliłem dla systemu Jenkins CI. Polecenia te powinny być uruchamiane w skrypcie natychmiast po zakończeniu polecenia xcodebuild archive.

BUILD_DIR="${WORKSPACE}/build"
XCODE_SCHEME="myscheme"

# Common path and partial filename
ARCHIVE_BASEPATH="${HOME}/Library/Developer/Xcode/Archives/$(date +%Y-%m-%d)/${XCODE_SCHEME}"

# Find the latest .xcarchive for the given scheme
NEW_ARCHIVE=$(ls -td "${ARCHIVE_BASEPATH}"* | head -n 1)

# Zip it up so non-Apple systems won't treat it as a dir
pushd "${NEW_ARCHIVE%/*}"
zip -r "${BUILD_DIR}/${NEW_ARCHIVE##*/}.zip" "${NEW_ARCHIVE##*/}"
popd

# Optional, disk cleanup
rm -rf "${NEW_ARCHIVE}"

BUILD_DIR służy do zbierania artefaktów, dzięki czemu można je łatwo archiwizować z Jenkinsa za pomocą globu, takiego jak build/*.ipa,build/*.zip

 2
Author: phatblat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-12 14:51:18

Podobne do innych, ale może trochę prostsze, ponieważ staram się nagrać lokalizację pliku .xcarchive. (Nie przenoszę również folderu archiwum, więc będzie to działać lepiej, jeśli robisz wiele kompilacji w tym samym czasie.)

Mój skrypt kompilacji wywołującej generuje nowy plik tempfile i ustawia jego ścieżkę do zmiennej środowiskowej o nazwie XCARCHIVE_PATH_TMPFILE. Ta zmienna środowiskowa jest dostępna w moim Scheme ' s Archive post-action shell script, który następnie zapisuje .ścieżka xcarchive do tego pliku. Budowa skrypt, który może odczytać ten plik po wywołaniu xcodebuild archive.

post-action shell script

echo $ARCHIVE_PATH > "$XCARCHIVE_PATH_TMPFILE"
 0
Author: zekel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-20 21:05:49

Na Xcode 4.6 możliwe jest określenie akcji post-build dla schematu, który ma być skompilowany do xcarchive:

echo "ARCHIVE_PATH=\"$ARCHIVE_PATH\"" > $PROJECT_DIR/archive_paths.sh

Skrypt kompilacji może być użyty do sprawdzenia, czy $ARCHIVE_PATH jest zdefiniowane po uruchomieniu xcodebuild i jeśli tak jest, wyjście xcarchive może zostać przeniesione do wyznaczonego folderu.

Ta metoda nie jest zbyt łatwa do utrzymania, jeśli cele w projekcie są duże, ponieważ dla każdego z nich konieczne jest oznaczanie odpowiedniego schematu jako 'shared' i dodawanie akcja post-build.

Aby rozwiązać ten problem, stworzyłem skrypt build, który programowo generuje ścieżkę archiwum poprzez rozpakowanie ostatniej kompilacji, która pasuje do docelowej nazwy w bieżącym dniu. Metoda ta działa niezawodnie tak długo, jak długo na maszynie nie działa wiele kompilacji o tej samej nazwie docelowej (może to być problem w środowiskach produkcyjnych, w których uruchamianych jest wiele jednoczesnych kompilacji).

#!/bin/bash
#
# Script to archive an existing xcode project to a target location.
# The script checks for a post-build action that defines the $ARCHIVE_PATH as follows:
# echo "ARCHIVE_PATH=\"$ARCHIVE_PATH\"" > $PROJECT_DIR/archive_paths.sh
# If such post-build action does not exist or sourcing it doesn't define the $ARCHIVE_PATH   
# variable, the script tries to generate it programmatically by finding the latest build
# in the expected archiving folder
#

post_build_script=archive_paths.sh
build_errors_file=build_errors.log
OUTPUT=output/
XCODEBUILD_CMD='/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild'
TARGET_SDK=iphoneos

function archive()
{
    echo "Archiving target '$1'"

    # Delete $post_build_script if it already exists as it should be generated by a 
    # post-build action
    rm -f $post_build_script

    # Use custom provisioning profile and code sign identity if specified, otherwise
    # default to project settings
    # Note: xcodebuild always returns 0 even if the build failed. We look for failure in
    # the stderr output instead
    if [[ ! -z "$2" ]] && [[ ! -z "$3" ]]; then 
        ${XCODEBUILD_CMD} clean archive -scheme $1 -sdk "${TARGET_SDK}" \
        "CODE_SIGN_IDENTITY=$3" "PROVISIONING_PROFILE=$2" 2>$build_errors_file
    else
        ${XCODEBUILD_CMD} clean archive -scheme $1 -sdk "${TARGET_SDK}"
        2>$build_errors_file  
    fi

    errors=`grep -wc "The following build commands failed" $build_errors_file`
    if [ "$errors" != "0" ]
    then
        echo "BUILD FAILED. Error Log:"
        cat $build_errors_file
        rm $build_errors_file
        exit 1
    fi
    rm $build_errors_file

    # Check if archive_paths.sh exists
    if [ -f "$post_build_script" ]; then
        source "$post_build_script"
        if [ -z "$ARCHIVE_PATH" ]; then
            echo "'$post_build_script' exists but ARCHIVE_PATH was not set.
              Enabling auto-detection" 
        fi
    fi
    if [ -z "$ARCHIVE_PATH" ]; then
        # This is the format of the xcarchive path:
        # /Users/$USER/Library/Developer/Xcode/Archives/`date +%Y-%m-%d`/$1\ 
        # `date +%d-%m-%Y\ %H.%M`.xcarchive
        # In order to avoid mismatches with the hour/minute of creation of the archive and
        # the current time, we list all archives with the correct target that have been
        # built in the current day (this may fail if the build wraps around midnight) and
        # fetch the correct file with a combination of ls and grep.
        # This script can break only if there are multiple targets with exactly the same
        # name running at the same time.
        EXTRACTED_LINE=$(ls -lrt /Users/$USER/Library/Developer/Xcode/Archives/`date
          +%Y-%m-%d`/ | grep $1\ `date +%d-%m-%Y` | tail -n 1)
        if [ "$EXTRACTED_LINE" == "" ]; then
            echo "Error: couldn't fetch archive path"
            exit 1
        fi
        # ls -lrt prints lines with the following format
        # drwxr-xr-x  5 mario  1306712193  170 25 Jul 17:17 ArchiveTest 25-07-2013
        #   17.17.xcarchive
        # We can split this line with the " " separator and take the latest bit:
        #   17.17.xcarchive
        FILE_NAME_SUFFIX=$(echo $EXTRACTED_LINE | awk '{split($0,a," "); print a[11]}')
        if [ "$FILE_NAME_SUFFIX" == "" ]; then
            echo "Error: couldn't fetch archive path"
            exit 1
        fi
        # Finally, we can put everything together to generate the path to the xcarchive
        ARCHIVE_PATH="/Users/$USER/Library/Developer/Xcode/Archives/`date 
          +%Y-%m-%d`/$1 `date +%d-%m-%Y` $FILE_NAME_SUFFIX/"
    fi

    # Create output folder if it doesn't already exist
    mkdir -p "$OUTPUT"

    # Move archived xcarchive build to designated output folder
    mv -v "$ARCHIVE_PATH" "$OUTPUT"
}


# Check number of command line args
if [ $# -lt 1 ]; then
    echo "Syntax: `basename $0` <target name> [/path/to/provisioning-profile]
      [<code sign identity]"
    exit 1
fi

if [ ! -z "$2" ]; then
    PROVISIONING_PROFILE="$2"
fi

if [ ! -z "$3" ]; then
    SIGN_PROVISIONING_PROFILE="$3"
else
    if [ ! -z "$PROVISIONING_PROFILE" ]; then
        SIGN_PROVISIONING_PROFILE=$(cat "$PROVISIONING_PROFILE" | egrep -a -o
          '[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}')
    fi
fi


archive "$1" "$PROVISIONING_PROFILE" "$SIGN_PROVISIONING_PROFILE"

Pełny kod źródłowy z przykładowym projektem Xcode można znaleźć tutaj:

Https://github.com/bizz84/Xcode-xcarchive-command

 0
Author: bizz84,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-29 10:22:50