Taskfile.yml 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. version: '3'
  2. includes:
  3. common: ../Taskfile.yml
  4. # Platform selection:
  5. # task ios:package -> simulator bundle (ad-hoc signed)
  6. # task ios:package IOS_PLATFORM=device CODESIGN_IDENTITY="Apple Development: ..." \
  7. # [PROVISIONING_PROFILE=path.mobileprovision]
  8. # -> device bundle (real signing)
  9. # task ios:package:ipa IOS_PLATFORM=device ... -> .ipa for distribution/install
  10. # For automatically managed signing & provisioning, use `task ios:xcode` and
  11. # build from the generated Xcode project instead.
  12. vars:
  13. BUNDLE_ID: '{{.BUNDLE_ID | default "com.wails.app"}}'
  14. IOS_PLATFORM: '{{.IOS_PLATFORM | default "simulator"}}'
  15. MIN_IOS_VERSION: '{{.MIN_IOS_VERSION | default "15.0"}}'
  16. IOS_SDK: '{{if eq .IOS_PLATFORM "device"}}iphoneos{{else}}iphonesimulator{{end}}'
  17. IOS_TARGET: 'arm64-apple-ios{{.MIN_IOS_VERSION}}{{if ne .IOS_PLATFORM "device"}}-simulator{{end}}'
  18. IOS_VERSION_MIN_FLAG: '{{if eq .IOS_PLATFORM "device"}}-miphoneos-version-min={{.MIN_IOS_VERSION}}{{else}}-mios-simulator-version-min={{.MIN_IOS_VERSION}}{{end}}'
  19. # "-" is ad-hoc signing: fine for the simulator, rejected by real devices.
  20. CODESIGN_IDENTITY: '{{.CODESIGN_IDENTITY | default "-"}}'
  21. # Optional path to a .mobileprovision embedded into device bundles
  22. PROVISIONING_PROFILE: '{{.PROVISIONING_PROFILE | default ""}}'
  23. # Optional entitlements applied at codesign time (generated default below)
  24. ENTITLEMENTS_FILE: '{{.ENTITLEMENTS_FILE | default "build/ios/entitlements.plist"}}'
  25. # SDK_PATH is computed lazily at task-level to avoid errors on non-macOS systems
  26. tasks:
  27. install:deps:
  28. summary: Check and install iOS development dependencies
  29. cmds:
  30. - go run build/ios/scripts/deps/install_deps.go
  31. env:
  32. TASK_FORCE_YES: '{{if .YES}}true{{else}}false{{end}}'
  33. prompt: This will check and install iOS development dependencies. Continue?
  34. # Note: Bindings generation may show CGO warnings for iOS C imports.
  35. # These warnings are harmless and don't affect the generated bindings,
  36. # as the generator only needs to parse Go types, not C implementations.
  37. build:
  38. summary: Creates a build of the application for iOS
  39. deps:
  40. - task: generate:ios:overlay
  41. - task: generate:ios:xcode
  42. - task: common:go:mod:tidy
  43. - task: common:build:frontend
  44. vars:
  45. BUILD_FLAGS:
  46. ref: .BUILD_FLAGS
  47. PRODUCTION:
  48. ref: .PRODUCTION
  49. cmds:
  50. - echo "Building iOS app {{.APP_NAME}} ({{.IOS_PLATFORM}})..."
  51. - go build -buildmode=c-archive -overlay build/ios/xcode/overlay.json {{.BUILD_FLAGS}} -o {{.OUTPUT}}.a
  52. vars:
  53. BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production,ios -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-tags ios,debug -buildvcs=false -gcflags=all="-l"{{end}}'
  54. DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}'
  55. OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}'
  56. SDK_PATH:
  57. sh: xcrun --sdk {{.IOS_SDK}} --show-sdk-path
  58. env:
  59. GOOS: ios
  60. CGO_ENABLED: 1
  61. GOARCH: '{{.ARCH | default "arm64"}}'
  62. PRODUCTION: '{{.PRODUCTION | default "false"}}'
  63. CGO_CFLAGS: '-isysroot {{.SDK_PATH}} -target {{.IOS_TARGET}} {{.IOS_VERSION_MIN_FLAG}}'
  64. CGO_LDFLAGS: '-isysroot {{.SDK_PATH}} -target {{.IOS_TARGET}}'
  65. package:
  66. summary: Packages a production build of the application into a `.app` bundle
  67. deps:
  68. - task: build
  69. vars:
  70. PRODUCTION: "true"
  71. cmds:
  72. - task: compile:ios:link
  73. - task: create:app:bundle
  74. package:ipa:
  75. summary: Packages a production build into an .ipa (requires IOS_PLATFORM=device and a real CODESIGN_IDENTITY)
  76. deps:
  77. - task: package
  78. cmds:
  79. - rm -rf "{{.BIN_DIR}}/Payload" "{{.BIN_DIR}}/{{.APP_NAME}}.ipa"
  80. - mkdir -p "{{.BIN_DIR}}/Payload"
  81. - cp -R "{{.BIN_DIR}}/{{.APP_NAME}}.app" "{{.BIN_DIR}}/Payload/"
  82. - cd "{{.BIN_DIR}}" && zip -qry "{{.APP_NAME}}.ipa" Payload
  83. - rm -rf "{{.BIN_DIR}}/Payload"
  84. - echo "Created {{.BIN_DIR}}/{{.APP_NAME}}.ipa"
  85. # Link the Go c-archive with main.m into the final executable.
  86. # Used by both the dev `run` flow and `package`.
  87. compile:ios:link:
  88. internal: true
  89. summary: Link the iOS executable from the Go archive and main.m
  90. vars:
  91. SDK_PATH:
  92. sh: xcrun --sdk {{.IOS_SDK}} --show-sdk-path
  93. cmds:
  94. - |
  95. MAIN_M=build/ios/xcode/main/main.m
  96. if [ ! -f "$MAIN_M" ]; then
  97. MAIN_M=build/ios/main.m
  98. fi
  99. xcrun -sdk {{.IOS_SDK}} clang \
  100. -target {{.IOS_TARGET}} \
  101. -isysroot {{.SDK_PATH}} \
  102. -framework Foundation -framework UIKit -framework WebKit \
  103. -framework Security -framework CoreFoundation \
  104. -framework UniformTypeIdentifiers \
  105. -framework LocalAuthentication -framework UserNotifications \
  106. -framework AVFoundation \
  107. -framework CoreLocation -framework CoreMotion -framework SystemConfiguration \
  108. -lresolv \
  109. -o "{{.BIN_DIR}}/{{.APP_NAME | lower}}" \
  110. "$MAIN_M" -Wl,-force_load,"{{.BIN_DIR}}/{{.APP_NAME}}.a"
  111. create:app:bundle:
  112. summary: Creates an iOS `.app` bundle
  113. cmds:
  114. - rm -rf "{{.BIN_DIR}}/{{.APP_NAME}}.app"
  115. - mkdir -p "{{.BIN_DIR}}/{{.APP_NAME}}.app"
  116. - cp "{{.BIN_DIR}}/{{.APP_NAME | lower}}" "{{.BIN_DIR}}/{{.APP_NAME}}.app/"
  117. - cp build/ios/Info.plist "{{.BIN_DIR}}/{{.APP_NAME}}.app/"
  118. - |
  119. # Embed the provisioning profile for device builds when provided
  120. if [ "{{.IOS_PLATFORM}}" = "device" ] && [ -n "{{.PROVISIONING_PROFILE}}" ]; then
  121. cp "{{.PROVISIONING_PROFILE}}" "{{.BIN_DIR}}/{{.APP_NAME}}.app/embedded.mobileprovision"
  122. fi
  123. - |
  124. # Compile asset catalog and embed icons in the app bundle
  125. APP_BUNDLE="{{.BIN_DIR}}/{{.APP_NAME}}.app"
  126. AC_IN="build/ios/xcode/main/Assets.xcassets"
  127. if [ -d "$AC_IN" ]; then
  128. TMP_AC=$(mktemp -d)
  129. xcrun actool \
  130. --compile "$TMP_AC" \
  131. --app-icon AppIcon \
  132. --platform {{.IOS_SDK}} \
  133. --minimum-deployment-target {{.MIN_IOS_VERSION}} \
  134. --product-type com.apple.product-type.application \
  135. --target-device iphone \
  136. --target-device ipad \
  137. --output-partial-info-plist "$APP_BUNDLE/assetcatalog_generated_info.plist" \
  138. "$AC_IN"
  139. if [ -f "$TMP_AC/Assets.car" ]; then
  140. cp -f "$TMP_AC/Assets.car" "$APP_BUNDLE/Assets.car"
  141. fi
  142. rm -rf "$TMP_AC"
  143. if [ -f "$APP_BUNDLE/assetcatalog_generated_info.plist" ]; then
  144. /usr/libexec/PlistBuddy -c "Merge $APP_BUNDLE/assetcatalog_generated_info.plist" "$APP_BUNDLE/Info.plist" || true
  145. fi
  146. fi
  147. - |
  148. # Sign the bundle. Ad-hoc ("-") works on the simulator only; device
  149. # builds need a real identity (and usually entitlements + profile).
  150. # Entitlements are only applied to device builds - simulator bundles
  151. # must not carry them (SpringBoard refuses to launch the app).
  152. if [ "{{.IOS_PLATFORM}}" = "device" ] && [ -f "{{.ENTITLEMENTS_FILE}}" ]; then
  153. codesign --force --sign "{{.CODESIGN_IDENTITY}}" --entitlements "{{.ENTITLEMENTS_FILE}}" "{{.BIN_DIR}}/{{.APP_NAME}}.app"
  154. else
  155. codesign --force --sign "{{.CODESIGN_IDENTITY}}" "{{.BIN_DIR}}/{{.APP_NAME}}.app"
  156. fi
  157. deploy-simulator:
  158. summary: Deploy to iOS Simulator
  159. deps: [package]
  160. cmds:
  161. - |
  162. # Read the real bundle id from the built bundle (it comes from
  163. # build/config.yml, not the BUNDLE_ID var, so it varies per project).
  164. APP="{{.BIN_DIR}}/{{.APP_NAME}}.app"
  165. BUNDLE_ID=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$APP/Info.plist")
  166. xcrun simctl terminate booted "$BUNDLE_ID" 2>/dev/null || true
  167. xcrun simctl uninstall booted "$BUNDLE_ID" 2>/dev/null || true
  168. xcrun simctl install booted "$APP"
  169. xcrun simctl launch booted "$BUNDLE_ID"
  170. deploy-device:
  171. summary: Install and launch on a connected device (requires IOS_PLATFORM=device build and real signing)
  172. cmds:
  173. - |
  174. DEVICE="{{.DEVICE_ID}}"
  175. if [ -z "$DEVICE" ]; then
  176. DEVICE=$(xcrun devicectl list devices --hide-headers 2>/dev/null | awk 'NR==1 {print $3}')
  177. fi
  178. if [ -z "$DEVICE" ]; then
  179. echo "No connected device found. Pass DEVICE_ID=<identifier> (see: xcrun devicectl list devices)"
  180. exit 1
  181. fi
  182. xcrun devicectl device install app --device "$DEVICE" "{{.BIN_DIR}}/{{.APP_NAME}}.app"
  183. BUNDLE_ID=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "{{.BIN_DIR}}/{{.APP_NAME}}.app/Info.plist")
  184. xcrun devicectl device process launch --device "$DEVICE" "$BUNDLE_ID"
  185. compile:ios:
  186. summary: Compile the iOS executable from Go archive and main.m
  187. deps:
  188. - task: build
  189. cmds:
  190. - task: compile:ios:link
  191. generate:ios:bindings:
  192. internal: true
  193. summary: Generates bindings for iOS with proper CGO flags
  194. sources:
  195. - "**/*.go"
  196. - go.mod
  197. - go.sum
  198. generates:
  199. - frontend/bindings/**/*
  200. vars:
  201. SDK_PATH:
  202. sh: xcrun --sdk {{.IOS_SDK}} --show-sdk-path
  203. cmds:
  204. - wails3 generate bindings -f '{{.BUILD_FLAGS}}' -clean=true
  205. env:
  206. GOOS: ios
  207. CGO_ENABLED: 1
  208. GOARCH: '{{.ARCH | default "arm64"}}'
  209. CGO_CFLAGS: '-isysroot {{.SDK_PATH}} -target {{.IOS_TARGET}} {{.IOS_VERSION_MIN_FLAG}}'
  210. CGO_LDFLAGS: '-isysroot {{.SDK_PATH}} -target {{.IOS_TARGET}}'
  211. ensure-simulator:
  212. internal: true
  213. summary: Ensure iOS Simulator is running and booted
  214. silent: true
  215. cmds:
  216. - |
  217. if ! xcrun simctl list devices booted | grep -q "Booted"; then
  218. echo "Starting iOS Simulator..."
  219. # Get first available iPhone device
  220. DEVICE_ID=$(xcrun simctl list devices available | grep "iPhone" | head -1 | grep -o "[A-F0-9-]\{36\}" || true)
  221. if [ -z "$DEVICE_ID" ]; then
  222. echo "No iPhone simulator found. Creating one..."
  223. RUNTIME=$(xcrun simctl list runtimes | grep iOS | tail -1 | awk '{print $NF}')
  224. # Use the newest iPhone device type this Xcode knows about
  225. DEVICE_TYPE=$(xcrun simctl list devicetypes | grep -oE "iPhone [0-9][^(]*" | tail -1 | xargs)
  226. DEVICE_ID=$(xcrun simctl create "Wails iPhone" "${DEVICE_TYPE:-iPhone 15}" "$RUNTIME")
  227. fi
  228. # Boot the device
  229. echo "Booting device $DEVICE_ID..."
  230. xcrun simctl boot "$DEVICE_ID" 2>/dev/null || true
  231. # Open Simulator app
  232. open -a Simulator
  233. # Wait for boot (max 30 seconds)
  234. for i in {1..30}; do
  235. if xcrun simctl list devices booted | grep -q "Booted"; then
  236. echo "Simulator booted successfully"
  237. break
  238. fi
  239. sleep 1
  240. done
  241. # Final check
  242. if ! xcrun simctl list devices booted | grep -q "Booted"; then
  243. echo "Failed to boot simulator after 30 seconds"
  244. exit 1
  245. fi
  246. fi
  247. preconditions:
  248. - sh: command -v xcrun
  249. msg: "xcrun not found. Please run 'wails3 task ios:install:deps' to install iOS development dependencies"
  250. generate:ios:overlay:
  251. internal: true
  252. summary: Generate Go build overlay and iOS shim
  253. sources:
  254. - build/config.yml
  255. generates:
  256. - build/ios/xcode/overlay.json
  257. - build/ios/xcode/gen/main_ios.gen.go
  258. cmds:
  259. - wails3 ios overlay:gen -out build/ios/xcode/overlay.json -config build/config.yml
  260. generate:ios:xcode:
  261. internal: true
  262. summary: Generate iOS Xcode project structure and assets
  263. sources:
  264. - build/config.yml
  265. - build/appicon.png
  266. generates:
  267. - build/ios/xcode/main/main.m
  268. - build/ios/xcode/main/Assets.xcassets/**/*
  269. - build/ios/xcode/project.pbxproj
  270. cmds:
  271. - wails3 ios xcode:gen -outdir build/ios/xcode -config build/config.yml
  272. run:
  273. summary: Run the application in iOS Simulator
  274. deps:
  275. - task: ensure-simulator
  276. - task: compile:ios
  277. cmds:
  278. - rm -rf "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app"
  279. - mkdir -p "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app"
  280. - cp "{{.BIN_DIR}}/{{.APP_NAME | lower}}" "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/{{.APP_NAME | lower}}"
  281. - cp build/ios/Info.dev.plist "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Info.plist"
  282. - |
  283. # Compile asset catalog and embed icons for dev bundle
  284. APP_BUNDLE="{{.BIN_DIR}}/{{.APP_NAME}}.dev.app"
  285. AC_IN="build/ios/xcode/main/Assets.xcassets"
  286. if [ -d "$AC_IN" ]; then
  287. TMP_AC=$(mktemp -d)
  288. xcrun actool \
  289. --compile "$TMP_AC" \
  290. --app-icon AppIcon \
  291. --platform iphonesimulator \
  292. --minimum-deployment-target {{.MIN_IOS_VERSION}} \
  293. --product-type com.apple.product-type.application \
  294. --target-device iphone \
  295. --target-device ipad \
  296. --output-partial-info-plist "$APP_BUNDLE/assetcatalog_generated_info.plist" \
  297. "$AC_IN"
  298. if [ -f "$TMP_AC/Assets.car" ]; then
  299. cp -f "$TMP_AC/Assets.car" "$APP_BUNDLE/Assets.car"
  300. fi
  301. rm -rf "$TMP_AC"
  302. if [ -f "$APP_BUNDLE/assetcatalog_generated_info.plist" ]; then
  303. /usr/libexec/PlistBuddy -c "Merge $APP_BUNDLE/assetcatalog_generated_info.plist" "$APP_BUNDLE/Info.plist" || true
  304. fi
  305. fi
  306. - codesign --force --sign - "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app"
  307. - |
  308. # The dev bundle id comes from build/ios/Info.dev.plist (derived from
  309. # build/config.yml), so read it from the built bundle rather than
  310. # assuming a com.wails.* prefix.
  311. DEV_APP="{{.BIN_DIR}}/{{.APP_NAME}}.dev.app"
  312. BUNDLE_ID=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$DEV_APP/Info.plist")
  313. xcrun simctl terminate booted "$BUNDLE_ID" 2>/dev/null || true
  314. xcrun simctl uninstall booted "$BUNDLE_ID" 2>/dev/null || true
  315. xcrun simctl install booted "$DEV_APP"
  316. xcrun simctl launch booted "$BUNDLE_ID"
  317. xcode:
  318. summary: Open the generated Xcode project for this app
  319. cmds:
  320. - task: generate:ios:xcode
  321. - open build/ios/xcode/main.xcodeproj
  322. logs:
  323. summary: Stream iOS Simulator logs filtered to this app
  324. cmds:
  325. - |
  326. xcrun simctl spawn booted log stream \
  327. --level debug \
  328. --style compact \
  329. --predicate 'senderImagePath CONTAINS[c] "{{.APP_NAME | lower}}.app/" OR composedMessage CONTAINS[c] "{{.APP_NAME | lower}}" OR eventMessage CONTAINS[c] "{{.APP_NAME | lower}}" OR process == "{{.APP_NAME | lower}}" OR category CONTAINS[c] "{{.APP_NAME | lower}}"'
  330. logs:dev:
  331. summary: Stream logs for the dev bundle (used by `task ios:run`)
  332. cmds:
  333. - |
  334. xcrun simctl spawn booted log stream \
  335. --level debug \
  336. --style compact \
  337. --predicate 'senderImagePath CONTAINS[c] ".dev.app/" OR subsystem == "com.wails.{{.APP_NAME | lower}}.dev" OR process == "{{.APP_NAME | lower}}"'
  338. logs:wide:
  339. summary: Wide log stream to help discover the exact process/bundle identifiers
  340. cmds:
  341. - |
  342. xcrun simctl spawn booted log stream \
  343. --level debug \
  344. --style compact \
  345. --predicate 'senderImagePath CONTAINS[c] ".app/"'