github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/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: 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 {tmpdir + "/main/Images.xcassets/LaunchImage.launchimage/Contents.json", []byte(contentsLaunchJSON)}, 48 {tmpdir + "/main/assets/Contents.json", []byte(contentsAssetsJSON)}, 49 } 50 51 52 53 for _, file := range files { 54 if err := mkdir(filepath.Dir(file.name)); err != nil { 55 return nil, err 56 } 57 if buildX { 58 printcmd("echo \"%s\" > %s", file.contents, file.name) 59 } 60 if !buildN { 61 if err := ioutil.WriteFile(file.name, file.contents, 0644); err != nil { 62 return nil, err 63 } 64 } 65 } 66 67 icons := []string{"Icon-iOS7@2x.png", "Icon.png", "Icon72x72.png", "Icon72x72@2x.png", "Icon76x76.png", "Icon76x76@2x.png", "Icon@2x.png"} 68 for _, icon := range icons { 69 copyFile(tmpdir + "/main/Images.xcassets/AppIcon.appiconset/" + icon, pkg.Dir + "/" + icon); 70 } 71 launchImages := []string{"LaunchImage.png"} 72 for _, launchImage := range launchImages { 73 copyFile(tmpdir + "/main/Images.xcassets/LaunchImage.launchimage/" + launchImage, pkg.Dir + "/" + launchImage); 74 } 75 76 armPath := filepath.Join(tmpdir, "arm") 77 if err := goBuild(src, darwinArmEnv, "-tags=ios", "-o="+armPath); err != nil { 78 return nil, err 79 } 80 nmpkgs, err := extractPkgs(darwinArmNM, armPath) 81 if err != nil { 82 return nil, err 83 } 84 85 arm64Path := filepath.Join(tmpdir, "arm64") 86 if err := goBuild(src, darwinArm64Env, "-tags=ios", "-o="+arm64Path); err != nil { 87 return nil, err 88 } 89 90 // Apple requires builds to target both darwin/arm and darwin/arm64. 91 // We are using lipo tool to build multiarchitecture binaries. 92 // TODO(jbd): Investigate the new announcements about iO9's fat binary 93 // size limitations are breaking this feature. 94 cmd := exec.Command( 95 "xcrun", "lipo", 96 "-create", armPath, arm64Path, 97 "-o", filepath.Join(tmpdir, "main/main"), 98 ) 99 if err := runCmd(cmd); err != nil { 100 return nil, err 101 } 102 103 // TODO(jbd): Set the launcher icon. 104 if err := iosCopyAssets(pkg, tmpdir); err != nil { 105 return nil, err 106 } 107 108 /*cmd = exec.Command( 109 "cp", "-R", 110 tmpdir, "/Users/admin/mainapp/", 111 ) 112 if err := runCmd(cmd); err != nil { 113 return nil, err 114 }*/ 115 116 // Build and move the release build to the output directory. 117 cmd = exec.Command( 118 "xcrun", "xcodebuild", 119 "-configuration", "Release", 120 "-project", tmpdir+"/main.xcodeproj", 121 ) 122 if err := runCmd(cmd); err != nil { 123 return nil, err 124 } 125 126 // TODO(jbd): Fallback to copying if renaming fails. 127 if buildO == "" { 128 buildO = path.Base(pkg.ImportPath) + ".app" 129 } 130 if buildX { 131 printcmd("mv %s %s", tmpdir+"/build/Release-iphoneos/main.app", buildO) 132 } 133 if !buildN { 134 // if output already exists, remove. 135 if err := os.RemoveAll(buildO); err != nil { 136 return nil, err 137 } 138 if err := os.Rename(tmpdir+"/build/Release-iphoneos/main.app", buildO); err != nil { 139 return nil, err 140 } 141 } 142 return nmpkgs, nil 143 } 144 145 func iosCopyAssets(pkg *build.Package, xcodeProjDir string) error { 146 147 dstAssets := xcodeProjDir + "/main/assets" 148 if err := mkdir(dstAssets); err != nil { 149 return err 150 } 151 152 srcAssets := filepath.Join(pkg.Dir, "assets") 153 fi, err := os.Stat(srcAssets) 154 if err != nil { 155 if os.IsNotExist(err) { 156 // skip walking through the directory to deep copy. 157 return nil 158 } 159 return err 160 } 161 if !fi.IsDir() { 162 // skip walking through to deep copy. 163 return nil 164 } 165 166 return filepath.Walk(srcAssets, func(path string, info os.FileInfo, err error) error { 167 if err != nil { 168 return err 169 } 170 if info.IsDir() { 171 return nil 172 } 173 dst := dstAssets + "/" + path[len(srcAssets)+1:] 174 return copyFile(dst, path) 175 }) 176 } 177 178 type infoplistTmplData struct { 179 BundleID string 180 Name string 181 } 182 183 var infoplistTmpl = template.Must(template.New("infoplist").Parse(`<?xml version="1.0" encoding="UTF-8"?> 184 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 185 <plist version="1.0"> 186 <dict> 187 <key>CFBundleDevelopmentRegion</key> 188 <string>en</string> 189 <key>CFBundleExecutable</key> 190 <string>main</string> 191 <key>CFBundleIdentifier</key> 192 <string>{{.BundleID}}</string> 193 <key>CFBundleInfoDictionaryVersion</key> 194 <string>6.0</string> 195 <key>CFBundleName</key> 196 <string>{{.Name}}</string> 197 <key>CFBundlePackageType</key> 198 <string>APPL</string> 199 <key>CFBundleShortVersionString</key> 200 <string>1.0</string> 201 <key>CFBundleSignature</key> 202 <string>????</string> 203 <key>CFBundleVersion</key> 204 <string>1</string> 205 <key>LSRequiresIPhoneOS</key> 206 <true/> 207 <key>UILaunchStoryboardName</key> 208 <string>LaunchScreen</string> 209 <key>UIRequiredDeviceCapabilities</key> 210 <array> 211 <string>armv7</string> 212 </array> 213 <key>UISupportedInterfaceOrientations</key> 214 <array> 215 <string>UIInterfaceOrientationPortrait</string> 216 <string>UIInterfaceOrientationLandscapeLeft</string> 217 <string>UIInterfaceOrientationLandscapeRight</string> 218 </array> 219 <key>UISupportedInterfaceOrientations~ipad</key> 220 <array> 221 <string>UIInterfaceOrientationPortrait</string> 222 <string>UIInterfaceOrientationPortraitUpsideDown</string> 223 <string>UIInterfaceOrientationLandscapeLeft</string> 224 <string>UIInterfaceOrientationLandscapeRight</string> 225 </array> 226 <key>UIBackgroundModes</key> 227 <array> 228 <string>fetch</string> 229 <string>remote-notification</string> 230 </array> 231 <key>NSLocationWhenInUseUsageDescription</key> 232 <string>Getting the GPS Location</string> 233 <key>NSAppTransportSecurity</key> 234 <dict> 235 <key>NSAllowsArbitraryLoads</key> 236 <true/> 237 </dict> 238 <key>UILaunchImages</key> 239 <array> 240 <dict> 241 <key>UILaunchImageMinimumOSVersion</key> 242 <string>8.0</string> 243 <key>UILaunchImageName</key> 244 <string>assets/LaunchImage.png</string> 245 <key>UILaunchImageOrientation</key> 246 <string>Portrait</string> 247 <key>UILaunchImageSize</key> 248 <string>{320, 480}</string> 249 </dict> 250 </array> 251 </dict> 252 </plist> 253 `)) 254 255 const projPbxproj = `// !$*UTF8*$! 256 { 257 archiveVersion = 1; 258 classes = { 259 }; 260 objectVersion = 46; 261 objects = { 262 263 /* Begin PBXBuildFile section */ 264 254BB84F1B1FD08900C56DE9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 254BB84E1B1FD08900C56DE9 /* Images.xcassets */; }; 265 254BB8681B1FD16500C56DE9 /* main in Resources */ = {isa = PBXBuildFile; fileRef = 254BB8671B1FD16500C56DE9 /* main */; }; 266 25FB30331B30FDEE0005924C /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 25FB30321B30FDEE0005924C /* assets */; }; 267 /* End PBXBuildFile section */ 268 269 /* Begin PBXFileReference section */ 270 254BB83E1B1FD08900C56DE9 /* main.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = main.app; sourceTree = BUILT_PRODUCTS_DIR; }; 271 254BB8421B1FD08900C56DE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 272 254BB84E1B1FD08900C56DE9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; 273 254BB8671B1FD16500C56DE9 /* main */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = main; sourceTree = "<group>"; }; 274 25FB30321B30FDEE0005924C /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = main/assets; sourceTree = "<group>"; }; 275 /* End PBXFileReference section */ 276 277 /* Begin PBXGroup section */ 278 254BB8351B1FD08900C56DE9 = { 279 isa = PBXGroup; 280 children = ( 281 25FB30321B30FDEE0005924C /* assets */, 282 254BB8401B1FD08900C56DE9 /* main */, 283 254BB83F1B1FD08900C56DE9 /* Products */, 284 ); 285 sourceTree = "<group>"; 286 usesTabs = 0; 287 }; 288 254BB83F1B1FD08900C56DE9 /* Products */ = { 289 isa = PBXGroup; 290 children = ( 291 254BB83E1B1FD08900C56DE9 /* main.app */, 292 ); 293 name = Products; 294 sourceTree = "<group>"; 295 }; 296 254BB8401B1FD08900C56DE9 /* main */ = { 297 isa = PBXGroup; 298 children = ( 299 254BB8671B1FD16500C56DE9 /* main */, 300 254BB84E1B1FD08900C56DE9 /* Images.xcassets */, 301 254BB8411B1FD08900C56DE9 /* Supporting Files */, 302 ); 303 path = main; 304 sourceTree = "<group>"; 305 }; 306 254BB8411B1FD08900C56DE9 /* Supporting Files */ = { 307 isa = PBXGroup; 308 children = ( 309 254BB8421B1FD08900C56DE9 /* Info.plist */, 310 ); 311 name = "Supporting Files"; 312 sourceTree = "<group>"; 313 }; 314 /* End PBXGroup section */ 315 316 /* Begin PBXNativeTarget section */ 317 254BB83D1B1FD08900C56DE9 /* main */ = { 318 isa = PBXNativeTarget; 319 buildConfigurationList = 254BB8611B1FD08900C56DE9 /* Build configuration list for PBXNativeTarget "main" */; 320 buildPhases = ( 321 254BB83C1B1FD08900C56DE9 /* Resources */, 322 ); 323 buildRules = ( 324 ); 325 dependencies = ( 326 ); 327 name = main; 328 productName = main; 329 productReference = 254BB83E1B1FD08900C56DE9 /* main.app */; 330 productType = "com.apple.product-type.application"; 331 }; 332 /* End PBXNativeTarget section */ 333 334 /* Begin PBXProject section */ 335 254BB8361B1FD08900C56DE9 /* Project object */ = { 336 isa = PBXProject; 337 attributes = { 338 LastUpgradeCheck = 0630; 339 ORGANIZATIONNAME = Developer; 340 TargetAttributes = { 341 254BB83D1B1FD08900C56DE9 = { 342 CreatedOnToolsVersion = 6.3.1; 343 }; 344 }; 345 }; 346 buildConfigurationList = 254BB8391B1FD08900C56DE9 /* Build configuration list for PBXProject "main" */; 347 compatibilityVersion = "Xcode 3.2"; 348 developmentRegion = English; 349 hasScannedForEncodings = 0; 350 knownRegions = ( 351 en, 352 Base, 353 ); 354 mainGroup = 254BB8351B1FD08900C56DE9; 355 productRefGroup = 254BB83F1B1FD08900C56DE9 /* Products */; 356 projectDirPath = ""; 357 projectRoot = ""; 358 targets = ( 359 254BB83D1B1FD08900C56DE9 /* main */, 360 ); 361 }; 362 /* End PBXProject section */ 363 364 /* Begin PBXResourcesBuildPhase section */ 365 254BB83C1B1FD08900C56DE9 /* Resources */ = { 366 isa = PBXResourcesBuildPhase; 367 buildActionMask = 2147483647; 368 files = ( 369 25FB30331B30FDEE0005924C /* assets in Resources */, 370 254BB8681B1FD16500C56DE9 /* main in Resources */, 371 254BB84F1B1FD08900C56DE9 /* Images.xcassets in Resources */, 372 ); 373 runOnlyForDeploymentPostprocessing = 0; 374 }; 375 /* End PBXResourcesBuildPhase section */ 376 377 /* Begin XCBuildConfiguration section */ 378 254BB85F1B1FD08900C56DE9 /* Debug */ = { 379 isa = XCBuildConfiguration; 380 buildSettings = { 381 ALWAYS_SEARCH_USER_PATHS = NO; 382 CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 383 CLANG_CXX_LIBRARY = "libc++"; 384 CLANG_ENABLE_MODULES = YES; 385 CLANG_ENABLE_OBJC_ARC = YES; 386 CLANG_WARN_BOOL_CONVERSION = YES; 387 CLANG_WARN_CONSTANT_CONVERSION = YES; 388 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 389 CLANG_WARN_EMPTY_BODY = YES; 390 CLANG_WARN_ENUM_CONVERSION = YES; 391 CLANG_WARN_INT_CONVERSION = YES; 392 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 393 CLANG_WARN_UNREACHABLE_CODE = YES; 394 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 395 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 396 COPY_PHASE_STRIP = NO; 397 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 398 ENABLE_STRICT_OBJC_MSGSEND = YES; 399 GCC_C_LANGUAGE_STANDARD = gnu99; 400 GCC_DYNAMIC_NO_PIC = NO; 401 GCC_NO_COMMON_BLOCKS = YES; 402 GCC_OPTIMIZATION_LEVEL = 0; 403 GCC_PREPROCESSOR_DEFINITIONS = ( 404 "DEBUG=1", 405 "$(inherited)", 406 ); 407 GCC_SYMBOLS_PRIVATE_EXTERN = NO; 408 GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 409 GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 410 GCC_WARN_UNDECLARED_SELECTOR = YES; 411 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 412 GCC_WARN_UNUSED_FUNCTION = YES; 413 GCC_WARN_UNUSED_VARIABLE = YES; 414 IPHONEOS_DEPLOYMENT_TARGET = 8.3; 415 MTL_ENABLE_DEBUG_INFO = YES; 416 ONLY_ACTIVE_ARCH = YES; 417 SDKROOT = iphoneos; 418 TARGETED_DEVICE_FAMILY = "1,2"; 419 }; 420 name = Debug; 421 }; 422 254BB8601B1FD08900C56DE9 /* Release */ = { 423 isa = XCBuildConfiguration; 424 buildSettings = { 425 ALWAYS_SEARCH_USER_PATHS = NO; 426 CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 427 CLANG_CXX_LIBRARY = "libc++"; 428 CLANG_ENABLE_MODULES = YES; 429 CLANG_ENABLE_OBJC_ARC = YES; 430 CLANG_WARN_BOOL_CONVERSION = YES; 431 CLANG_WARN_CONSTANT_CONVERSION = YES; 432 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 433 CLANG_WARN_EMPTY_BODY = YES; 434 CLANG_WARN_ENUM_CONVERSION = YES; 435 CLANG_WARN_INT_CONVERSION = YES; 436 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 437 CLANG_WARN_UNREACHABLE_CODE = YES; 438 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 439 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 440 COPY_PHASE_STRIP = NO; 441 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 442 ENABLE_NS_ASSERTIONS = NO; 443 ENABLE_STRICT_OBJC_MSGSEND = YES; 444 GCC_C_LANGUAGE_STANDARD = gnu99; 445 GCC_NO_COMMON_BLOCKS = YES; 446 GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 447 GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 448 GCC_WARN_UNDECLARED_SELECTOR = YES; 449 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 450 GCC_WARN_UNUSED_FUNCTION = YES; 451 GCC_WARN_UNUSED_VARIABLE = YES; 452 IPHONEOS_DEPLOYMENT_TARGET = 8.3; 453 MTL_ENABLE_DEBUG_INFO = NO; 454 SDKROOT = iphoneos; 455 TARGETED_DEVICE_FAMILY = "1,2"; 456 VALIDATE_PRODUCT = YES; 457 }; 458 name = Release; 459 }; 460 254BB8621B1FD08900C56DE9 /* Debug */ = { 461 isa = XCBuildConfiguration; 462 buildSettings = { 463 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 464 INFOPLIST_FILE = main/Info.plist; 465 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 466 PRODUCT_NAME = "$(TARGET_NAME)"; 467 }; 468 name = Debug; 469 }; 470 254BB8631B1FD08900C56DE9 /* Release */ = { 471 isa = XCBuildConfiguration; 472 buildSettings = { 473 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 474 INFOPLIST_FILE = main/Info.plist; 475 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 476 PRODUCT_NAME = "$(TARGET_NAME)"; 477 }; 478 name = Release; 479 }; 480 /* End XCBuildConfiguration section */ 481 482 /* Begin XCConfigurationList section */ 483 254BB8391B1FD08900C56DE9 /* Build configuration list for PBXProject "main" */ = { 484 isa = XCConfigurationList; 485 buildConfigurations = ( 486 254BB85F1B1FD08900C56DE9 /* Debug */, 487 254BB8601B1FD08900C56DE9 /* Release */, 488 ); 489 defaultConfigurationIsVisible = 0; 490 defaultConfigurationName = Release; 491 }; 492 254BB8611B1FD08900C56DE9 /* Build configuration list for PBXNativeTarget "main" */ = { 493 isa = XCConfigurationList; 494 buildConfigurations = ( 495 254BB8621B1FD08900C56DE9 /* Debug */, 496 254BB8631B1FD08900C56DE9 /* Release */, 497 ); 498 defaultConfigurationIsVisible = 0; 499 defaultConfigurationName = Release; 500 }; 501 /* End XCConfigurationList section */ 502 }; 503 rootObject = 254BB8361B1FD08900C56DE9 /* Project object */; 504 } 505 ` 506 507 508 const contentsJSON = `{ 509 "images" : [ 510 { 511 "idiom" : "iphone", 512 "size" : "29x29", 513 "scale" : "1x" 514 }, 515 { 516 "idiom" : "iphone", 517 "size" : "29x29", 518 "scale" : "2x" 519 }, 520 { 521 "idiom" : "iphone", 522 "size" : "40x40", 523 "scale" : "2x" 524 }, 525 { 526 "size" : "57x57", 527 "idiom" : "iphone", 528 "filename" : "Icon.png", 529 "scale" : "1x" 530 }, 531 { 532 "size" : "57x57", 533 "idiom" : "iphone", 534 "filename" : "Icon@2x.png", 535 "scale" : "2x" 536 }, 537 { 538 "size" : "60x60", 539 "idiom" : "iphone", 540 "filename" : "Icon-iOS7@2x.png", 541 "scale" : "2x" 542 }, 543 { 544 "idiom" : "ipad", 545 "size" : "29x29", 546 "scale" : "1x" 547 }, 548 { 549 "idiom" : "ipad", 550 "size" : "29x29", 551 "scale" : "2x" 552 }, 553 { 554 "idiom" : "ipad", 555 "size" : "40x40", 556 "scale" : "1x" 557 }, 558 { 559 "idiom" : "ipad", 560 "size" : "40x40", 561 "scale" : "2x" 562 }, 563 { 564 "idiom" : "ipad", 565 "size" : "50x50", 566 "scale" : "1x" 567 }, 568 { 569 "idiom" : "ipad", 570 "size" : "50x50", 571 "scale" : "2x" 572 }, 573 { 574 "size" : "72x72", 575 "idiom" : "ipad", 576 "filename" : "Icon72x72.png", 577 "scale" : "1x" 578 }, 579 { 580 "size" : "72x72", 581 "idiom" : "ipad", 582 "filename" : "Icon72x72@2x.png", 583 "scale" : "2x" 584 }, 585 { 586 "size" : "76x76", 587 "idiom" : "ipad", 588 "filename" : "Icon76x76.png", 589 "scale" : "1x" 590 }, 591 { 592 "size" : "76x76", 593 "idiom" : "ipad", 594 "filename" : "Icon76x76@2x.png", 595 "scale" : "2x" 596 } 597 ], 598 "info" : { 599 "version" : 1, 600 "author" : "xcode" 601 } 602 } 603 ` 604 605 const contentsLaunchJSON = `{ 606 "images" : [ 607 { 608 "orientation" : "portrait", 609 "idiom" : "iphone", 610 "extent" : "full-screen", 611 "minimum-system-version" : "7.0", 612 "filename" : "LaunchImage.png", 613 "scale" : "1x" 614 } 615 ], 616 "info" : { 617 "version" : 1, 618 "author" : "xcode" 619 } 620 } 621 ` 622 const contentsAssetsJSON = `{ 623 "images" : [ 624 { 625 "idiom" : "iphone", 626 "filename" : "LaunchImage.png", 627 "scale" : "1x" 628 } 629 ], 630 "info" : { 631 "version" : 1, 632 "author" : "xcode" 633 } 634 } 635 ` 636 637 // rfc1034Label sanitizes the name to be usable in a uniform type identifier. 638 // The sanitization is similar to xcode's rfc1034identifier macro that 639 // replaces illegal characters (not conforming the rfc1034 label rule) with '-'. 640 func rfc1034Label(name string) string { 641 // * Uniform type identifier: 642 // 643 // According to 644 // https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html 645 // 646 // A uniform type identifier is a Unicode string that usually contains characters 647 // in the ASCII character set. However, only a subset of the ASCII characters are 648 // permitted. You may use the Roman alphabet in upper and lower case (A–Z, a–z), 649 // the digits 0 through 9, the dot (“.”), and the hyphen (“-”). This restriction 650 // is based on DNS name restrictions, set forth in RFC 1035. 651 // 652 // Uniform type identifiers may also contain any of the Unicode characters greater 653 // than U+007F. 654 // 655 // Note: the actual implementation of xcode does not allow some unicode characters 656 // greater than U+007f. In this implementation, we just replace everything non 657 // alphanumeric with "-" like the rfc1034identifier macro. 658 // 659 // * RFC1034 Label 660 // 661 // <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ] 662 // <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str> 663 // <let-dig-hyp> ::= <let-dig> | "-" 664 // <let-dig> ::= <letter> | <digit> 665 const surrSelf = 0x10000 666 begin := false 667 668 var res []rune 669 for i, r := range name { 670 if r == '.' && !begin { 671 continue 672 } 673 begin = true 674 675 switch { 676 case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z': 677 res = append(res, r) 678 case '0' <= r && r <= '9': 679 if i == 0 { 680 res = append(res, '-') 681 } else { 682 res = append(res, r) 683 } 684 default: 685 if r < surrSelf { 686 res = append(res, '-') 687 } else { 688 res = append(res, '-', '-') 689 } 690 } 691 } 692 return string(res) 693 }