Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xcodebuild is not compiling the project unless it is opened using Xcode atleast only once for cocoapods integrated project

I have a project with cocoa pods.

Here is the command that I use to build the project.

/usr/bin/xcodebuild -scheme Jenkins -workspace /Users/Shared/Jenkins/Documents/Jenkins/Jenkins2/Jenkins.xcworkspace -configuration Release clean build CONFIGURATION_BUILD_DIR=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/app 'CODE_SIGN_IDENTITY=iPhone Distribution: XXXX yay (3G5FKTZJ2K)' PRODUCT_BUNDLE_IDENTIFIER=com.XXXX.two PROVISIONING_PROFILE=6e6506e9-8233-4886-9084-ce21e8f8bbae

The above script works good only if the project has been opened using Xcode atleast once after that Xcode can be closed no issues.

If the project has not been opened then If I run the script the wheel is spinning below without any progress forever for example in the below image

problem

if its opened once instead of the spinning wheel below texts would be shown below

=== CLEAN TARGET XWebView OF PROJECT Pods WITH CONFIGURATION Release ===

Check dependencies

Clean.Remove clean /Users/Shared/Jenkins/Documents/JenkinsTestNuu/app/XWebView.framework.dSYM builtin-rm -rf /Users/Shared/Jenkins/Documents/JenkinsTestNuu/app/XWebView.framework.dSYM

Clean.Remove clean /Users/Shared/Jenkins/Library/Developer/Xcode/DerivedData/appanme-bqjwbjcqisegldeaonpytprisnig/Build/Intermediates/Pods.build/Release-iphoneos/XWebView.build builtin-rm -rf /Users/Shared/Jenkins/Library/Developer/Xcode/DerivedData/appanme-bqjwbjcqisegldeaonpytprisnig/Build/Intermediates/Pods.build/Release-iphoneos/XWebView.build

Clean.Remove clean /Users/Shared/Jenkins/Documents/JenkinsTestNuu/app/XWebView.framework builtin-rm -rf /Users/Shared/Jenkins/Documents/JenkinsTestNuu/app/XWebView.framework

=== CLEAN TARGET Pods OF PROJECT Pods WITH CONFIGURATION Release ===

Check dependencies etc...

The problem is not observed in any non-cocoapods project.

So what would be the cause and how to solve it ?

like image 456
Durai Amuthan.H Avatar asked Mar 30 '16 19:03

Durai Amuthan.H


2 Answers

Why it happens ?

A quick diffMerge tool analysis between a project opened by Xcode Vs a same project not opened by Xcode so far

Diff Merge tool analysis

From this we can see lots of scheme related files being created once opened by Xcode inside .xcodeproj

so xcodebuild creates .app by using the scheme related meta data but as there is no scheme to build against its failing

How to solve this ?

As there is lack of meta data it couldn't build and so it requires Xcode to be opened so that the files get automactically created by Xcode and so there will be some schemes to build against.

But when you open the Xcode this Scheme related files gets created under xcuserdata which is for particular user. i.e each user gets their own file that saves state folders opened,last file opened etc...

Its not wise idea to keep this file with us.

The problem can be solved by checking the Shared Check box under Manage Schemes

This moves schemes out from under your individual xcuserdata into a shared folder that can be committed via source control and you can safely ignore xcuserdata and keep the shared folder in source control

Manage schemes

Now we can build the code without opening the Xcode even for only one time.

Branding (UI,Build settings and functional)

  • UI
    • App icon & other icons
    • iTunes Artwork
  • Build settings
    • App Name
    • Bundle Identifier
    • Provisioning profile
    • Code signing identity
  • Functional
    • Brand specific URLs(login,logout,resource-fetch etc...)

Using Terminal

Branding.sh

   #Author: Durai Amuthan([email protected])
#This is to achieve multiple branding of an iOS app by configuring the variables below

#************ Configuring the brand starts ************

#Directory path where .xcworkspace or .xcodeproj exists
PathOfProjectDirectory=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/New/

#Path where info.plist exists
PathOfInfoPlist=$PathOfProjectDirectory/XxYyZz

#Path to icons where new iTunesArtwork and application icon exixts
#Note: Make sure proper naming conventions of file has been followed
PathOfNewIcons=/Users/Shared/Jenkins/Documents/icons-two

#Path to asset resource where you have kept your application icon.
PathOfAppIconSet=$PathOfProjectDirectory/XxYyZz/Icon.xcassets/AppIcon.appiconset

#Path where do you want the .app file has to be kept
PathToApp=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/app

#Path where do you want the .ipa file has to kept
PathToIpa=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/ipa

#Cocoapods project or project that involves more than one modules are scheme based
isWorkspaceBased=true

#Path of the Project (.xcodeproj) - applicable for workspace(.xcworkspace) based project
PathofProjectFile=$PathOfProjectDirectory/XxYyZz.xcodeproj

#Path of the Workspace (.xcworkspace)
PathofWorkspaceFile=$PathOfProjectDirectory/XxYyZz.xcworkspace

#Name of the target - applicable only for non-workspace(.xcodeproj)  based projects
Target=XxYyZz

#Scheme of the iOS app
Scheme=XxYyZz

#To ascertain Cocoapods has been used or not
isCocoaPodsBased=true

#Configuration of the app (Debug -(Development) or Release(Adhoc or Distribution))
Config=Release

#For giving access to signing idetity found in KeyChain
LoginKeychainPath=/Users/Shared/Jenkins/Library/Keychains/login.keychain
LoginKeyChainPassword=xxyyzz

#Name of the code signing identity.You can find the name in Keychain or xcode build setting
CodeSigningIdentity='iPhone Distribution: Xx Yy Zz Limited (3Z5MHUYJ2L)'

#Path of the provisioning profile
PathToMobileProvision=/Users/Shared/Jenkins/Desktop/BrandingTest.mobileprovision

#UUID value found inside Provisioning profile has to be given
#Do not forget to install provisiong profile in the system
ProvisioningProfileIdentity=6e6506e9-8233-4886-9084-zf21e8f8bbae

#Bundle identifier of the app
BundleIdentifier=com.xxyy.zz

#AppVersion of the app
AppVersion=2.2.2

#App Name
Appname=Two

#************ Configuring the brand ends ************

#** Creatting the build based on configuration starts **

cd $PathOfInfoPlist
echo "****************** Setting App Name ******************"
/usr/libexec/PlistBuddy -c "Set :CFBundleName $Appname" info.plist
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $Appname" info.plist
echo "app name has been set as $Appname"

cd $PathOfProjectDirectory
echo "****************** Setting AppVersion ******************"
/usr/bin/agvtool new-marketing-AppVersion $AppVersion
/usr/bin/agvtool new-AppVersion -all $AppVersion

echo "****************** Changing app icons & iTunes Artwork ******************"
cp -R $PathOfNewIcons/*.png $PathOfAppIconSet
echo "App icons has been changed at $PathOfNewIcons"
cp -R $PathOfNewIcons/iTunesArtwork@2x $PathOfProjectDirectory/XxYyZz
cp -R $PathOfNewIcons/iTunesArtwork $PathOfProjectDirectory/XxYyZz
echo "iTunesArtwork has been changed at $PathOfProjectDirectory"

#Unlock login keychain
security unlock-keychain -p $LoginKeyChainPassword $LoginKeychainPath
if $isCocoaPodsBased == 'true'
then
echo "****************** Installing Cocoapods **********************"
/usr/local/bin/pod install
echo "Cocoapods has been installed"
fi

echo "****************** Creating .app ******************"
if $isWorkspaceBased == 'true'
then
/usr/bin/xcodebuild -scheme $Scheme -workspace $PathofWorkspaceFile -configuration $Config clean build CONFIGURATION_BUILD_DIR=$PathToApp "CODE_SIGN_IDENTITY=$CodeSigningIdentity" "PRODUCT_BUNDLE_IDENTIFIER=$BundleIdentifier" "PROVISIONING_PROFILE=$ProvisioningProfileIdentity"
else
/usr/bin/xcodebuild -target $Target -project $PathofProjectFile -configuration $Config clean build CONFIGURATION_BUILD_DIR=$PathToApp "CODE_SIGN_IDENTITY=$CodeSigningIdentity" "PRODUCT_BUNDLE_IDENTIFIER=$BundleIdentifier" "PROVISIONING_PROFILE=$ProvisioningProfileIdentity"
fi
echo ".app has been generated at $PathToApp"

echo "****************** Creating .ipa *******************"
/usr/bin/xcrun -sdk iphoneos PackageApplication -v $PathToApp/XxYyZz.app -o $PathToIpa/$Appname.ipa --embed $PathToMobileProvision --sign "$CodeSigningIdentity"
echo "$Appname.ipa has been generated at $PathToIpa"

#** Creatting the build based on configuration ends **

The file is self-descriptive you can understand easily. Just configure the values of variable in the file and call it like below

sh Branding.sh

FYI:

If you want some other icons also to be changed besides App Icon and iTunesArtwork use cp command e.g

cp path/to/source path/to/destination

To know more info do cp man

With the above file you can do Branding for UI and Build Settings.

For functional branding , you have to keep

  • Brand specific URLs

  • Other inputs respective to a brand

in a separate plist file so that this things also can be changed according to respective brand while building the app

Plist file

In coding side you can customise your application to read the values from plist like this

Function defintion:

func getPlistFile()->Dictionary<String,AnyObject>? {
        var dictPlistFile:Dictionary<String,AnyObject>?
        if let path = NSBundle.mainBundle().pathForResource("plistfile", ofType: "plist") {
            if let dictValue = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
                dictPlistFile=dictValue
            }
        }
        return dictPlistFile
    }

Function calling:

var Value=getPlistFile()?["Key"]

You can change the values of the key according to brand using the PlistBuddy while building the app

Here is the syntax

/usr/libexec/PlistBuddy -c "Set :Key Value" plistfile.plist

Using Jenkins

We can effectively re-use the shell script here in jenkins

1.You have to parameterise all the variables in shell script in jenkins using Add Parameter... like in the below screenshot I have done for one variable like that you have to do it for all others

Paramterization

2.Choose Execute shell in the Build Step

Build Step

3.Copy the script that is there in between Creating the build based on configuration starts and Creating the build based on configuration ends and paste it in Execute Shell Execute Shell

Note:

  • Resource Rules

    There is a known bug Regarding ResourceRules of Xcode in some versions while building and packaging the app through non-xcode interface.

    So it has to be run once to deactivate a validation for resource rules path in xcode.The resource rules path is deprecated feature and apple doesn't accept apps that comes with resource rules but if we build an app without using Xcode the validation error saying resource rules has not been found will arise to counter that we have to run the script only once.

xcode_fix_PackageApplicationResourceRules.sh

#!/bin/sh
# A script to patch xcrun PackageInstallation so that it doesn't use the deprecated --resource-rules
# See "Do not use the --resource-rules flag or ResourceRules.plist. They have been obsoleted and will be rejected."
#   under https://developer.apple.com/library/mac/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG205
# Reported as Apple bug #19384243
# 
# should be run as a user who can modify the PackageApplication file

xcodedir=$1

function usage {
        # FIXME we cannot parse args properly because 2 are optional...
        echo "USAGE: $0 xcodedir"
        echo "  xcodedir: an install dir like /Application/Xcode6.1.1.app"
}

if [[ $# -ne 1 ]]; then
        echo "ERROR: invalid number of arguments"
        usage
        exit -1 
fi

pi="$xcodedir/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication"
piorig="$piOrig"

if [[ ! -f "$pi" ]]; then
    echo "$pi file not found. Invalid argument ?"
    usage
    exit -1
fi

grep resource-rules "$pi" 
if [[ $? -ne 0 ]]; then
    echo "PackageApplication doesn't use resource-rules. Skipping"
    exit 0
fi

if [[ -f "$piorig" ]]; then
    echo "Backup file $piorig already exist. Aborting"
    exit -1
fi

perl  -p -i'Orig' -e 'BEGIN{undef $/;} s/,resource-rules(.*sign}).*ResourceRules.plist"/$1/smg' "$pi" 
echo $?
  • Unlock keychain

    Whenever you run Branding.sh in terminal it will prompt username and password as its accessing system keychain

    Whenever you run the Job in jenkins you will get "User Interaction Is Not Allowed" error

    so to tackle this you have to follow the below steps

    • Open the Keychain Access
    • Right click on the private key
    • Select "Get Info"
    • Select "Access Control" tab
    • Click "Allow all applications to access this item"
    • Click "Save Changes"
    • Enter your password
  • Provisioning profile

    if you ever get "No Matching Provisioning Profile Found" make sure you have double clicked and installed it via Xcode.

    The moment you install you'll see UUID.mobileprovision in ~/Library/MobileDevice/Provisioning Profiles/

    This UUID is the value inside mobile provision that means the provisioning profile is installed.

I hope this helps you

like image 88
Durai Amuthan.H Avatar answered Oct 25 '22 17:10

Durai Amuthan.H


You need to run pod install before building the project so that CocoaPods fetches the Pods specified in your Podfile within the Jenkins workspace.

like image 23
Christopher Orr Avatar answered Oct 25 '22 16:10

Christopher Orr