github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/cmd/gomobile/build_iosapp.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/build" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 "path" 15 "path/filepath" 16 "strings" 17 "text/template" 18 ) 19 20 func goIOSBuild(pkg *build.Package) (map[string]bool, error) { 21 src := pkg.ImportPath 22 if buildO != "" && !strings.HasSuffix(buildO, ".app") { 23 return nil, fmt.Errorf("-o must have an .app for target=ios") 24 } 25 26 productName := rfc1034Label(path.Base(pkg.ImportPath)) 27 if productName == "" { 28 productName = "ProductName" // like xcode. 29 } 30 31 infoplist := new(bytes.Buffer) 32 if err := infoplistTmpl.Execute(infoplist, infoplistTmplData{ 33 // TODO: better bundle id. 34 BundleID: "org.golang.todo." + productName, 35 Name: strings.Title(path.Base(pkg.ImportPath)), 36 }); err != nil { 37 return nil, err 38 } 39 40 files := []struct { 41 name string 42 contents []byte 43 }{ 44 {tmpdir + "/main.xcodeproj/project.pbxproj", []byte(projPbxproj)}, 45 {tmpdir + "/main/Info.plist", infoplist.Bytes()}, 46 {tmpdir + "/main/Images.xcassets/AppIcon.appiconset/Contents.json", []byte(contentsJSON)}, 47 } 48 49 for _, file := range files { 50 if err := mkdir(filepath.Dir(file.name)); err != nil { 51 return nil, err 52 } 53 if buildX { 54 printcmd("echo \"%s\" > %s", file.contents, file.name) 55 } 56 if !buildN { 57 if err := ioutil.WriteFile(file.name, file.contents, 0644); err != nil { 58 return nil, err 59 } 60 } 61 } 62 63 ctx.BuildTags = append(ctx.BuildTags, "ios") 64 65 armPath := filepath.Join(tmpdir, "arm") 66 if err := goBuild(src, darwinArmEnv, "-o="+armPath); err != nil { 67 return nil, err 68 } 69 nmpkgs, err := extractPkgs(darwinArmNM, armPath) 70 if err != nil { 71 return nil, err 72 } 73 74 arm64Path := filepath.Join(tmpdir, "arm64") 75 if err := goBuild(src, darwinArm64Env, "-o="+arm64Path); err != nil { 76 return nil, err 77 } 78 79 // Apple requires builds to target both darwin/arm and darwin/arm64. 80 // We are using lipo tool to build multiarchitecture binaries. 81 // TODO(jbd): Investigate the new announcements about iO9's fat binary 82 // size limitations are breaking this feature. 83 cmd := exec.Command( 84 "xcrun", "lipo", 85 "-create", armPath, arm64Path, 86 "-o", filepath.Join(tmpdir, "main/main"), 87 ) 88 if err := runCmd(cmd); err != nil { 89 return nil, err 90 } 91 92 // TODO(jbd): Set the launcher icon. 93 if err := iosCopyAssets(pkg, tmpdir); err != nil { 94 return nil, err 95 } 96 97 // Build and move the release build to the output directory. 98 cmd = exec.Command( 99 "xcrun", "xcodebuild", 100 "-configuration", "Release", 101 "-project", tmpdir+"/main.xcodeproj", 102 ) 103 if err := runCmd(cmd); err != nil { 104 return nil, err 105 } 106 107 // TODO(jbd): Fallback to copying if renaming fails. 108 if buildO == "" { 109 buildO = path.Base(pkg.ImportPath) + ".app" 110 } 111 if buildX { 112 printcmd("mv %s %s", tmpdir+"/build/Release-iphoneos/main.app", buildO) 113 } 114 if !buildN { 115 // if output already exists, remove. 116 if err := os.RemoveAll(buildO); err != nil { 117 return nil, err 118 } 119 if err := os.Rename(tmpdir+"/build/Release-iphoneos/main.app", buildO); err != nil { 120 return nil, err 121 } 122 } 123 return nmpkgs, nil 124 } 125 126 func iosCopyAssets(pkg *build.Package, xcodeProjDir string) error { 127 dstAssets := xcodeProjDir + "/main/assets" 128 if err := mkdir(dstAssets); err != nil { 129 return err 130 } 131 132 srcAssets := filepath.Join(pkg.Dir, "assets") 133 fi, err := os.Stat(srcAssets) 134 if err != nil { 135 if os.IsNotExist(err) { 136 // skip walking through the directory to deep copy. 137 return nil 138 } 139 return err 140 } 141 if !fi.IsDir() { 142 // skip walking through to deep copy. 143 return nil 144 } 145 // if assets is a symlink, follow the symlink. 146 srcAssets, err = filepath.EvalSymlinks(srcAssets) 147 if err != nil { 148 return err 149 } 150 return filepath.Walk(srcAssets, func(path string, info os.FileInfo, err error) error { 151 if err != nil { 152 return err 153 } 154 if name := filepath.Base(path); strings.HasPrefix(name, ".") { 155 // Do not include the hidden files. 156 return nil 157 } 158 if info.IsDir() { 159 return nil 160 } 161 dst := dstAssets + "/" + path[len(srcAssets)+1:] 162 return copyFile(dst, path) 163 }) 164 } 165 166 type infoplistTmplData struct { 167 BundleID string 168 Name string 169 } 170 171 var infoplistTmpl = template.Must(template.New("infoplist").Parse(`<?xml version="1.0" encoding="UTF-8"?> 172 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 173 <plist version="1.0"> 174 <dict> 175 <key>CFBundleDevelopmentRegion</key> 176 <string>en</string> 177 <key>CFBundleExecutable</key> 178 <string>main</string> 179 <key>CFBundleIdentifier</key> 180 <string>{{.BundleID}}</string> 181 <key>CFBundleInfoDictionaryVersion</key> 182 <string>6.0</string> 183 <key>CFBundleName</key> 184 <string>{{.Name}}</string> 185 <key>CFBundlePackageType</key> 186 <string>APPL</string> 187 <key>CFBundleShortVersionString</key> 188 <string>1.0</string> 189 <key>CFBundleSignature</key> 190 <string>????</string> 191 <key>CFBundleVersion</key> 192 <string>1</string> 193 <key>LSRequiresIPhoneOS</key> 194 <true/> 195 <key>UILaunchStoryboardName</key> 196 <string>LaunchScreen</string> 197 <key>UIRequiredDeviceCapabilities</key> 198 <array> 199 <string>armv7</string> 200 </array> 201 <key>UISupportedInterfaceOrientations</key> 202 <array> 203 <string>UIInterfaceOrientationPortrait</string> 204 <string>UIInterfaceOrientationLandscapeLeft</string> 205 <string>UIInterfaceOrientationLandscapeRight</string> 206 </array> 207 <key>UISupportedInterfaceOrientations~ipad</key> 208 <array> 209 <string>UIInterfaceOrientationPortrait</string> 210 <string>UIInterfaceOrientationPortraitUpsideDown</string> 211 <string>UIInterfaceOrientationLandscapeLeft</string> 212 <string>UIInterfaceOrientationLandscapeRight</string> 213 </array> 214 </dict> 215 </plist> 216 `)) 217 218 const projPbxproj = `// !$*UTF8*$! 219 { 220 archiveVersion = 1; 221 classes = { 222 }; 223 objectVersion = 46; 224 objects = { 225 226 /* Begin PBXBuildFile section */ 227 254BB84F1B1FD08900C56DE9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 254BB84E1B1FD08900C56DE9 /* Images.xcassets */; }; 228 254BB8681B1FD16500C56DE9 /* main in Resources */ = {isa = PBXBuildFile; fileRef = 254BB8671B1FD16500C56DE9 /* main */; }; 229 25FB30331B30FDEE0005924C /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 25FB30321B30FDEE0005924C /* assets */; }; 230 /* End PBXBuildFile section */ 231 232 /* Begin PBXFileReference section */ 233 254BB83E1B1FD08900C56DE9 /* main.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = main.app; sourceTree = BUILT_PRODUCTS_DIR; }; 234 254BB8421B1FD08900C56DE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 235 254BB84E1B1FD08900C56DE9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; 236 254BB8671B1FD16500C56DE9 /* main */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = main; sourceTree = "<group>"; }; 237 25FB30321B30FDEE0005924C /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = main/assets; sourceTree = "<group>"; }; 238 /* End PBXFileReference section */ 239 240 /* Begin PBXGroup section */ 241 254BB8351B1FD08900C56DE9 = { 242 isa = PBXGroup; 243 children = ( 244 25FB30321B30FDEE0005924C /* assets */, 245 254BB8401B1FD08900C56DE9 /* main */, 246 254BB83F1B1FD08900C56DE9 /* Products */, 247 ); 248 sourceTree = "<group>"; 249 usesTabs = 0; 250 }; 251 254BB83F1B1FD08900C56DE9 /* Products */ = { 252 isa = PBXGroup; 253 children = ( 254 254BB83E1B1FD08900C56DE9 /* main.app */, 255 ); 256 name = Products; 257 sourceTree = "<group>"; 258 }; 259 254BB8401B1FD08900C56DE9 /* main */ = { 260 isa = PBXGroup; 261 children = ( 262 254BB8671B1FD16500C56DE9 /* main */, 263 254BB84E1B1FD08900C56DE9 /* Images.xcassets */, 264 254BB8411B1FD08900C56DE9 /* Supporting Files */, 265 ); 266 path = main; 267 sourceTree = "<group>"; 268 }; 269 254BB8411B1FD08900C56DE9 /* Supporting Files */ = { 270 isa = PBXGroup; 271 children = ( 272 254BB8421B1FD08900C56DE9 /* Info.plist */, 273 ); 274 name = "Supporting Files"; 275 sourceTree = "<group>"; 276 }; 277 /* End PBXGroup section */ 278 279 /* Begin PBXNativeTarget section */ 280 254BB83D1B1FD08900C56DE9 /* main */ = { 281 isa = PBXNativeTarget; 282 buildConfigurationList = 254BB8611B1FD08900C56DE9 /* Build configuration list for PBXNativeTarget "main" */; 283 buildPhases = ( 284 254BB83C1B1FD08900C56DE9 /* Resources */, 285 ); 286 buildRules = ( 287 ); 288 dependencies = ( 289 ); 290 name = main; 291 productName = main; 292 productReference = 254BB83E1B1FD08900C56DE9 /* main.app */; 293 productType = "com.apple.product-type.application"; 294 }; 295 /* End PBXNativeTarget section */ 296 297 /* Begin PBXProject section */ 298 254BB8361B1FD08900C56DE9 /* Project object */ = { 299 isa = PBXProject; 300 attributes = { 301 LastUpgradeCheck = 0630; 302 ORGANIZATIONNAME = Developer; 303 TargetAttributes = { 304 254BB83D1B1FD08900C56DE9 = { 305 CreatedOnToolsVersion = 6.3.1; 306 }; 307 }; 308 }; 309 buildConfigurationList = 254BB8391B1FD08900C56DE9 /* Build configuration list for PBXProject "main" */; 310 compatibilityVersion = "Xcode 3.2"; 311 developmentRegion = English; 312 hasScannedForEncodings = 0; 313 knownRegions = ( 314 en, 315 Base, 316 ); 317 mainGroup = 254BB8351B1FD08900C56DE9; 318 productRefGroup = 254BB83F1B1FD08900C56DE9 /* Products */; 319 projectDirPath = ""; 320 projectRoot = ""; 321 targets = ( 322 254BB83D1B1FD08900C56DE9 /* main */, 323 ); 324 }; 325 /* End PBXProject section */ 326 327 /* Begin PBXResourcesBuildPhase section */ 328 254BB83C1B1FD08900C56DE9 /* Resources */ = { 329 isa = PBXResourcesBuildPhase; 330 buildActionMask = 2147483647; 331 files = ( 332 25FB30331B30FDEE0005924C /* assets in Resources */, 333 254BB8681B1FD16500C56DE9 /* main in Resources */, 334 254BB84F1B1FD08900C56DE9 /* Images.xcassets in Resources */, 335 ); 336 runOnlyForDeploymentPostprocessing = 0; 337 }; 338 /* End PBXResourcesBuildPhase section */ 339 340 /* Begin XCBuildConfiguration section */ 341 254BB8601B1FD08900C56DE9 /* Release */ = { 342 isa = XCBuildConfiguration; 343 buildSettings = { 344 ALWAYS_SEARCH_USER_PATHS = NO; 345 CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 346 CLANG_CXX_LIBRARY = "libc++"; 347 CLANG_ENABLE_MODULES = YES; 348 CLANG_ENABLE_OBJC_ARC = YES; 349 CLANG_WARN_BOOL_CONVERSION = YES; 350 CLANG_WARN_CONSTANT_CONVERSION = YES; 351 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 352 CLANG_WARN_EMPTY_BODY = YES; 353 CLANG_WARN_ENUM_CONVERSION = YES; 354 CLANG_WARN_INT_CONVERSION = YES; 355 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 356 CLANG_WARN_UNREACHABLE_CODE = YES; 357 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 358 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 359 COPY_PHASE_STRIP = NO; 360 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 361 ENABLE_NS_ASSERTIONS = NO; 362 ENABLE_STRICT_OBJC_MSGSEND = YES; 363 GCC_C_LANGUAGE_STANDARD = gnu99; 364 GCC_NO_COMMON_BLOCKS = YES; 365 GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 366 GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 367 GCC_WARN_UNDECLARED_SELECTOR = YES; 368 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 369 GCC_WARN_UNUSED_FUNCTION = YES; 370 GCC_WARN_UNUSED_VARIABLE = YES; 371 IPHONEOS_DEPLOYMENT_TARGET = 8.3; 372 MTL_ENABLE_DEBUG_INFO = NO; 373 SDKROOT = iphoneos; 374 TARGETED_DEVICE_FAMILY = "1,2"; 375 VALIDATE_PRODUCT = YES; 376 }; 377 name = Release; 378 }; 379 254BB8631B1FD08900C56DE9 /* Release */ = { 380 isa = XCBuildConfiguration; 381 buildSettings = { 382 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 383 INFOPLIST_FILE = main/Info.plist; 384 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 385 PRODUCT_NAME = "$(TARGET_NAME)"; 386 }; 387 name = Release; 388 }; 389 /* End XCBuildConfiguration section */ 390 391 /* Begin XCConfigurationList section */ 392 254BB8391B1FD08900C56DE9 /* Build configuration list for PBXProject "main" */ = { 393 isa = XCConfigurationList; 394 buildConfigurations = ( 395 254BB8601B1FD08900C56DE9 /* Release */, 396 ); 397 defaultConfigurationIsVisible = 0; 398 defaultConfigurationName = Release; 399 }; 400 254BB8611B1FD08900C56DE9 /* Build configuration list for PBXNativeTarget "main" */ = { 401 isa = XCConfigurationList; 402 buildConfigurations = ( 403 254BB8631B1FD08900C56DE9 /* Release */, 404 ); 405 defaultConfigurationIsVisible = 0; 406 defaultConfigurationName = Release; 407 }; 408 /* End XCConfigurationList section */ 409 }; 410 rootObject = 254BB8361B1FD08900C56DE9 /* Project object */; 411 } 412 ` 413 414 const contentsJSON = `{ 415 "images" : [ 416 { 417 "idiom" : "iphone", 418 "size" : "29x29", 419 "scale" : "2x" 420 }, 421 { 422 "idiom" : "iphone", 423 "size" : "29x29", 424 "scale" : "3x" 425 }, 426 { 427 "idiom" : "iphone", 428 "size" : "40x40", 429 "scale" : "2x" 430 }, 431 { 432 "idiom" : "iphone", 433 "size" : "40x40", 434 "scale" : "3x" 435 }, 436 { 437 "idiom" : "iphone", 438 "size" : "60x60", 439 "scale" : "2x" 440 }, 441 { 442 "idiom" : "iphone", 443 "size" : "60x60", 444 "scale" : "3x" 445 }, 446 { 447 "idiom" : "ipad", 448 "size" : "29x29", 449 "scale" : "1x" 450 }, 451 { 452 "idiom" : "ipad", 453 "size" : "29x29", 454 "scale" : "2x" 455 }, 456 { 457 "idiom" : "ipad", 458 "size" : "40x40", 459 "scale" : "1x" 460 }, 461 { 462 "idiom" : "ipad", 463 "size" : "40x40", 464 "scale" : "2x" 465 }, 466 { 467 "idiom" : "ipad", 468 "size" : "76x76", 469 "scale" : "1x" 470 }, 471 { 472 "idiom" : "ipad", 473 "size" : "76x76", 474 "scale" : "2x" 475 } 476 ], 477 "info" : { 478 "version" : 1, 479 "author" : "xcode" 480 } 481 } 482 ` 483 484 // rfc1034Label sanitizes the name to be usable in a uniform type identifier. 485 // The sanitization is similar to xcode's rfc1034identifier macro that 486 // replaces illegal characters (not conforming the rfc1034 label rule) with '-'. 487 func rfc1034Label(name string) string { 488 // * Uniform type identifier: 489 // 490 // According to 491 // https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html 492 // 493 // A uniform type identifier is a Unicode string that usually contains characters 494 // in the ASCII character set. However, only a subset of the ASCII characters are 495 // permitted. You may use the Roman alphabet in upper and lower case (A–Z, a–z), 496 // the digits 0 through 9, the dot (“.”), and the hyphen (“-”). This restriction 497 // is based on DNS name restrictions, set forth in RFC 1035. 498 // 499 // Uniform type identifiers may also contain any of the Unicode characters greater 500 // than U+007F. 501 // 502 // Note: the actual implementation of xcode does not allow some unicode characters 503 // greater than U+007f. In this implementation, we just replace everything non 504 // alphanumeric with "-" like the rfc1034identifier macro. 505 // 506 // * RFC1034 Label 507 // 508 // <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ] 509 // <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str> 510 // <let-dig-hyp> ::= <let-dig> | "-" 511 // <let-dig> ::= <letter> | <digit> 512 const surrSelf = 0x10000 513 begin := false 514 515 var res []rune 516 for i, r := range name { 517 if r == '.' && !begin { 518 continue 519 } 520 begin = true 521 522 switch { 523 case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z': 524 res = append(res, r) 525 case '0' <= r && r <= '9': 526 if i == 0 { 527 res = append(res, '-') 528 } else { 529 res = append(res, r) 530 } 531 default: 532 if r < surrSelf { 533 res = append(res, '-') 534 } else { 535 res = append(res, '-', '-') 536 } 537 } 538 } 539 return string(res) 540 }