Skip to content

Instantly share code, notes, and snippets.

@uhooi
Last active July 17, 2023 02:18
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save uhooi/f06f17a58973dc7834f0545cf87816e7 to your computer and use it in GitHub Desktop.
Save uhooi/f06f17a58973dc7834f0545cf87816e7 to your computer and use it in GitHub Desktop.
Makefile for iOS App development
PRODUCT_NAME := Foo
SCHEME_NAME := ${PRODUCT_NAME}
WORKSPACE_NAME := ${PRODUCT_NAME}.xcworkspace
UI_TESTS_TARGET_NAME := ${PRODUCT_NAME}UITests
TEST_SDK := iphonesimulator
TEST_CONFIGURATION := Debug
TEST_PLATFORM := iOS Simulator
TEST_DEVICE ?= iPhone 11 Pro Max
TEST_OS ?= 13.3
TEST_DESTINATION := 'platform=${TEST_PLATFORM},name=${TEST_DEVICE},OS=${TEST_OS}'
SDK := iphoneos
CONFIGURATION := Release
ARCHIVE_PATH := ./build/${PRODUCT_NAME}.xcarchive
EXPORT_PATH := ./output/${SDK}/${CONFIGURATION}
IPA_PATH := ${EXPORT_PATH}/${PRODUCT_NAME}.ipa
DEVELOPMENT_TEAM := XXXXXXXXXX
PRODUCT_BUNDLE_IDENTIFIER := com.example.Foo
ADHOC_PROVISIONING_PROFILE_SPECIFIER := Foo_AdHoc
ADHOC_EXPORT_OPTIONS_PATH := ./ExportOptions/ExportOptionsAdHoc.plist
APPSTORE_PROVISIONING_PROFILE_SPECIFIER := Foo_AppStore
APPSTORE_EXPORT_OPTIONS_PATH := ./ExportOptions/ExportOptionsAppStore.plist
MODULE_TEMPLATE_NAME ?= uhooi_viper
.DEFAULT_GOAL := help
.PHONY: help
help:
@grep -E '^[a-zA-Z_-]+:.*?# .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":[^#]*? #| #"}; {printf "%-57s%s\n", $$1 $$3, $$2}'
.PHONY: setup
setup: # Install dependencies and prepared development configuration
$(MAKE) install-bundler
$(MAKE) install-templates
$(MAKE) install-mint
$(MAKE) install-carthage
$(MAKE) generate-licenses
.PHONY: install-bundler
install-bundler: # Install Bundler dependencies
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
.PHONY: update-bundler
update-bundler: # Update Bundler dependencies
bundle config path vendor/bundle
bundle update --jobs 4 --retry 3
.PHONY: install-mint
install-mint: # Install Mint dependencies
mint bootstrap
.PHONY: install-cocoapods
install-cocoapods: # Install CocoaPods dependencies and generate workspace
bundle exec pod install
.PHONY: update-cocoapods
update-cocoapods: # Update CocoaPods dependencies and generate workspace
bundle exec pod update
.PHONY: install-carthage
install-carthage: # Install Carthage dependencies
mint run carthage carthage bootstrap --platform iOS --cache-builds
$(MAKE) show-carthage-dependencies
.PHONY: update-carthage
update-carthage: # Update Carthage dependencies
mint run carthage carthage update --platform iOS
$(MAKE) show-carthage-dependencies
.PHONY: show-carthage-dependencies
show-carthage-dependencies:
@echo '*** Resolved dependencies:'
@cat 'Cartfile.resolved'
.PHONY: install-templates
install-templates: # Install Generamba templates
bundle exec generamba template install
.PHONY: generate-licenses
generate-licenses: # Generate licenses with LicensePlist and regenerate project
mint run LicensePlist license-plist --output-path ${PRODUCT_NAME}/Settings.bundle
$(MAKE) generate-xcodeproj
.PHONY: generate-module
generate-module: # Generate module with Generamba and regenerate project # MODULE_NAME=[module name]
bundle exec generamba gen ${MODULE_NAME} ${MODULE_TEMPLATE_NAME}
$(MAKE) generate-xcodeproj
.PHONY: generate-xcodeproj
generate-xcodeproj: # Generate project with XcodeGen
mint run xcodegen xcodegen generate
$(MAKE) install-cocoapods
$(MAKE) open
.PHONY: open
open: # Open workspace in Xcode
open ./${WORKSPACE_NAME}
.PHONY: clean
clean: # Delete cache
xcodebuild clean -alltargets
rm -rf ./Pods
rm -rf ./Carthage
rm -rf ./vendor/bundle
rm -rf ./Templates
.PHONY: build-debug
build-debug: # Xcode build for debug
set -o pipefail && \
xcodebuild \
-sdk ${TEST_SDK} \
-configuration ${TEST_CONFIGURATION} \
-workspace ${WORKSPACE_NAME} \
-scheme ${SCHEME_NAME} \
build \
| bundle exec xcpretty
.PHONY: test
test: # Xcode test # TEST_DEVICE=[device] TEST_OS=[OS]
set -o pipefail && \
xcodebuild \
-sdk ${TEST_SDK} \
-configuration ${TEST_CONFIGURATION} \
-workspace ${WORKSPACE_NAME} \
-scheme ${SCHEME_NAME} \
-destination ${TEST_DESTINATION} \
-skip-testing:${UI_TESTS_TARGET_NAME} \
clean test \
| bundle exec xcpretty
.PHONY: show-devices
show-devices: # Show devices
instruments -s devices
.PHONY: generate-ipa-adhoc
generate-ipa-adhoc: # Generate IPA file for Ad Hoc
$(MAKE) archive-adhoc
$(MAKE) export-archive-adhoc
.PHONY: archive-adhoc
archive-adhoc:
$(MAKE) archive PROVISIONING_PROFILE_SPECIFIER=${ADHOC_PROVISIONING_PROFILE_SPECIFIER}
.PHONY: export-archive-adhoc
export-archive-adhoc:
$(MAKE) export-archive EXPORT_OPTIONS_PATH=${ADHOC_EXPORT_OPTIONS_PATH}
.PHONY: generate-ipa-appstore
generate-ipa-appstore: # Generate IPA file for App Store
$(MAKE) archive-appstore
$(MAKE) export-archive-appstore
.PHONY: archive-appstore
archive-appstore:
$(MAKE) archive PROVISIONING_PROFILE_SPECIFIER=${APPSTORE_PROVISIONING_PROFILE_SPECIFIER}
.PHONY: export-archive-appstore
export-archive-appstore:
$(MAKE) export-archive EXPORT_OPTIONS_PATH=${APPSTORE_EXPORT_OPTIONS_PATH}
.PHONY: archive
archive:
set -o pipefail && \
xcodebuild \
-sdk ${SDK} \
-configuration ${CONFIGURATION} \
-workspace ${WORKSPACE_NAME} \
-scheme ${SCHEME_NAME} \
-archivePath ${ARCHIVE_PATH} \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="iPhone Distribution" \
PROVISIONING_PROFILE_SPECIFIER=${PROVISIONING_PROFILE_SPECIFIER} \
DEVELOPMENT_TEAM=${DEVELOPMENT_TEAM} \
PRODUCT_BUNDLE_IDENTIFIER=${PRODUCT_BUNDLE_IDENTIFIER} \
clean archive \
| bundle exec xcpretty
.PHONY: export-archive
export-archive:
set -o pipefail && \
xcodebuild \
-exportArchive \
-archivePath ${ARCHIVE_PATH} \
-exportPath ${EXPORT_PATH} \
-exportOptionsPlist ${EXPORT_OPTIONS_PATH} \
| bundle exec xcpretty
.PHONY: validate
validate: # Validate IPA file # ASC_USERNAME=[user name] ASC_PASSWORD=[password]
xcrun altool --validate-app -f ${IPA_PATH} -t ios -u ${ASC_USERNAME} -p ${ASC_PASSWORD}
.PHONY: upload
upload: # Distribute IPA file to App Store Connect # ASC_USERNAME=[user name] ASC_PASSWORD=[password]
xcrun altool --upload-app -f ${IPA_PATH} -t ios -u ${ASC_USERNAME} -p ${ASC_PASSWORD}
@thara
Copy link

thara commented Jan 31, 2020

How would you like to use prerequisites like the following?

+setup: install-bundler install-mint install-carthage generate-xcodeproj # Install dependencies and prepared development configuration
-setup: # Install dependencies and prepared development configuration
-	make install-bundler
-	make install-mint
-	make install-carthage
-       make generate-xcodeproj

FYI

@uhooi
Copy link
Author

uhooi commented Feb 1, 2020

Thank you for your advice!
I understand a little about how to use prerequisites.

Similarly, we want to run install-cocoapods after install-bundler, so should we write:

- install-cocoapods:
+ install-cocoapods: install-bundler
	bundle exec pod install

I don't know how to rewrite generate-xcodeproj .

generate-xcodeproj:
	mint run xcodegen xcodegen generate
	make install-cocoapods
	make open

Is execution order not guaranteed when there are multiple dependent targets?

@thara
Copy link

thara commented Feb 1, 2020

I don't know how to rewrite generate-xcodeproj .

Make other target for running mint run xcodegen xcodegen generate and use prerequisites.

xcodegen:
	mint run xcodegen xcodegen generate

generate-xcodeproj: xcodegen install-cocoapods open

Is execution order not guaranteed when there are multiple dependent targets?

If you use make -j $N ( 1 < N), multiple prerequisites be executed in parallel.
To avoid this, you can separate bunch of commands to each targets and make each of prerequisites.

xcodegen:
	mint run xcodegen xcodegen generate

install-cocoapods: xcodegen
        .....

open: install-cocoapods
        .....

generate-xcodeproj: open

However, this approach has a problem that you can't just run one command without commands in prerequisites.
If you don't like this, you had to call make task recursively like your first resolution.

FYI.
Prefer using $(MAKE) to actual make.

.PHONY: generate-xcodeproj
generate-xcodeproj: # Generate project with XcodeGen
	mint run xcodegen xcodegen generate
	$(MAKE) install-cocoapods
	$(MAKE) open

@uhooi
Copy link
Author

uhooi commented Feb 1, 2020

thx!
I decide to call make task recursively.
I use $(MAKE) instead of make.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment