Non-main packages are built in an earlier code path than main packages. Add the ios build tag before that early code path to ensure packages that expect that tag successfully compiles. Fixes golang/go#25944 Change-Id: Ida15475109373127dde024037e9787c76b32ee0b Reviewed-on: https://go-review.googlesource.com/119555 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
594 lines
17 KiB
Go
594 lines
17 KiB
Go
// Copyright 2015 The Go Authors. All rights reserved.
|
||
// Use of this source code is governed by a BSD-style
|
||
// license that can be found in the LICENSE file.
|
||
|
||
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/x509"
|
||
"encoding/pem"
|
||
"fmt"
|
||
"go/build"
|
||
"io/ioutil"
|
||
"os"
|
||
"os/exec"
|
||
"path"
|
||
"path/filepath"
|
||
"strings"
|
||
"text/template"
|
||
)
|
||
|
||
func goIOSBuild(pkg *build.Package, bundleID string, archs []string) (map[string]bool, error) {
|
||
src := pkg.ImportPath
|
||
if buildO != "" && !strings.HasSuffix(buildO, ".app") {
|
||
return nil, fmt.Errorf("-o must have an .app for -target=ios")
|
||
}
|
||
|
||
productName := rfc1034Label(path.Base(pkg.ImportPath))
|
||
if productName == "" {
|
||
productName = "ProductName" // like xcode.
|
||
}
|
||
|
||
infoplist := new(bytes.Buffer)
|
||
if err := infoplistTmpl.Execute(infoplist, infoplistTmplData{
|
||
// TODO: better bundle id.
|
||
BundleID: bundleID + "." + productName,
|
||
Name: strings.Title(path.Base(pkg.ImportPath)),
|
||
}); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
files := []struct {
|
||
name string
|
||
contents []byte
|
||
}{
|
||
{tmpdir + "/main.xcodeproj/project.pbxproj", []byte(projPbxproj)},
|
||
{tmpdir + "/main/Info.plist", infoplist.Bytes()},
|
||
{tmpdir + "/main/Images.xcassets/AppIcon.appiconset/Contents.json", []byte(contentsJSON)},
|
||
}
|
||
|
||
for _, file := range files {
|
||
if err := mkdir(filepath.Dir(file.name)); err != nil {
|
||
return nil, err
|
||
}
|
||
if buildX {
|
||
printcmd("echo \"%s\" > %s", file.contents, file.name)
|
||
}
|
||
if !buildN {
|
||
if err := ioutil.WriteFile(file.name, file.contents, 0644); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
}
|
||
|
||
// We are using lipo tool to build multiarchitecture binaries.
|
||
cmd := exec.Command(
|
||
"xcrun", "lipo",
|
||
"-o", filepath.Join(tmpdir, "main/main"),
|
||
"-create",
|
||
)
|
||
var nmpkgs map[string]bool
|
||
for _, arch := range archs {
|
||
path := filepath.Join(tmpdir, arch)
|
||
// Disable DWARF; see golang.org/issues/25148.
|
||
if err := goBuild(src, darwinEnv[arch], "-ldflags=-w", "-o="+path); err != nil {
|
||
return nil, err
|
||
}
|
||
if nmpkgs == nil {
|
||
var err error
|
||
nmpkgs, err = extractPkgs(darwinArmNM, path)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
cmd.Args = append(cmd.Args, path)
|
||
}
|
||
|
||
if err := runCmd(cmd); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// TODO(jbd): Set the launcher icon.
|
||
if err := iosCopyAssets(pkg, tmpdir); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// Detect the team ID
|
||
teamID, err := detectTeamID()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// Build and move the release build to the output directory.
|
||
cmdStrings := []string{
|
||
"xcodebuild",
|
||
"-configuration", "Release",
|
||
"-project", tmpdir + "/main.xcodeproj",
|
||
"-allowProvisioningUpdates",
|
||
"DEVELOPMENT_TEAM=" + teamID,
|
||
}
|
||
|
||
cmd = exec.Command("xcrun", cmdStrings...)
|
||
if err := runCmd(cmd); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// TODO(jbd): Fallback to copying if renaming fails.
|
||
if buildO == "" {
|
||
n := pkg.ImportPath
|
||
if n == "." {
|
||
// use cwd name
|
||
cwd, err := os.Getwd()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("cannot create .app; cannot get the current working dir: %v", err)
|
||
}
|
||
n = cwd
|
||
}
|
||
n = path.Base(n)
|
||
buildO = n + ".app"
|
||
}
|
||
if buildX {
|
||
printcmd("mv %s %s", tmpdir+"/build/Release-iphoneos/main.app", buildO)
|
||
}
|
||
if !buildN {
|
||
// if output already exists, remove.
|
||
if err := os.RemoveAll(buildO); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := os.Rename(tmpdir+"/build/Release-iphoneos/main.app", buildO); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
return nmpkgs, nil
|
||
}
|
||
|
||
func detectTeamID() (string, error) {
|
||
// Grabs the first certificate for "iPhone Developer"; will not work if there
|
||
// are multiple certificates and the first is not desired.
|
||
cmd := exec.Command(
|
||
"security", "find-certificate",
|
||
"-c", "iPhone Developer", "-p",
|
||
)
|
||
pemString, err := cmd.Output()
|
||
if err != nil {
|
||
err = fmt.Errorf("failed to pull the signing certificate to determine your team ID: %v", err)
|
||
return "", err
|
||
}
|
||
|
||
block, _ := pem.Decode(pemString)
|
||
if block == nil {
|
||
err = fmt.Errorf("failed to decode the PEM to determine your team ID: %s", pemString)
|
||
return "", err
|
||
}
|
||
|
||
cert, err := x509.ParseCertificate(block.Bytes)
|
||
if err != nil {
|
||
err = fmt.Errorf("failed to parse your signing certificate to determine your team ID: %v", err)
|
||
return "", err
|
||
}
|
||
|
||
if len(cert.Subject.OrganizationalUnit) == 0 {
|
||
err = fmt.Errorf("the signing certificate has no organizational unit (team ID).")
|
||
return "", err
|
||
}
|
||
|
||
return cert.Subject.OrganizationalUnit[0], nil
|
||
}
|
||
|
||
func iosCopyAssets(pkg *build.Package, xcodeProjDir string) error {
|
||
dstAssets := xcodeProjDir + "/main/assets"
|
||
if err := mkdir(dstAssets); err != nil {
|
||
return err
|
||
}
|
||
|
||
srcAssets := filepath.Join(pkg.Dir, "assets")
|
||
fi, err := os.Stat(srcAssets)
|
||
if err != nil {
|
||
if os.IsNotExist(err) {
|
||
// skip walking through the directory to deep copy.
|
||
return nil
|
||
}
|
||
return err
|
||
}
|
||
if !fi.IsDir() {
|
||
// skip walking through to deep copy.
|
||
return nil
|
||
}
|
||
// if assets is a symlink, follow the symlink.
|
||
srcAssets, err = filepath.EvalSymlinks(srcAssets)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return filepath.Walk(srcAssets, func(path string, info os.FileInfo, err error) error {
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if name := filepath.Base(path); strings.HasPrefix(name, ".") {
|
||
// Do not include the hidden files.
|
||
return nil
|
||
}
|
||
if info.IsDir() {
|
||
return nil
|
||
}
|
||
dst := dstAssets + "/" + path[len(srcAssets)+1:]
|
||
return copyFile(dst, path)
|
||
})
|
||
}
|
||
|
||
type infoplistTmplData struct {
|
||
BundleID string
|
||
Name string
|
||
}
|
||
|
||
var infoplistTmpl = template.Must(template.New("infoplist").Parse(`<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>CFBundleDevelopmentRegion</key>
|
||
<string>en</string>
|
||
<key>CFBundleExecutable</key>
|
||
<string>main</string>
|
||
<key>CFBundleIdentifier</key>
|
||
<string>{{.BundleID}}</string>
|
||
<key>CFBundleInfoDictionaryVersion</key>
|
||
<string>6.0</string>
|
||
<key>CFBundleName</key>
|
||
<string>{{.Name}}</string>
|
||
<key>CFBundlePackageType</key>
|
||
<string>APPL</string>
|
||
<key>CFBundleShortVersionString</key>
|
||
<string>1.0</string>
|
||
<key>CFBundleSignature</key>
|
||
<string>????</string>
|
||
<key>CFBundleVersion</key>
|
||
<string>1</string>
|
||
<key>LSRequiresIPhoneOS</key>
|
||
<true/>
|
||
<key>UILaunchStoryboardName</key>
|
||
<string>LaunchScreen</string>
|
||
<key>UIRequiredDeviceCapabilities</key>
|
||
<array>
|
||
<string>armv7</string>
|
||
</array>
|
||
<key>UISupportedInterfaceOrientations</key>
|
||
<array>
|
||
<string>UIInterfaceOrientationPortrait</string>
|
||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||
</array>
|
||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||
<array>
|
||
<string>UIInterfaceOrientationPortrait</string>
|
||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||
</array>
|
||
</dict>
|
||
</plist>
|
||
`))
|
||
|
||
const projPbxproj = `// !$*UTF8*$!
|
||
{
|
||
archiveVersion = 1;
|
||
classes = {
|
||
};
|
||
objectVersion = 46;
|
||
objects = {
|
||
|
||
/* Begin PBXBuildFile section */
|
||
254BB84F1B1FD08900C56DE9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 254BB84E1B1FD08900C56DE9 /* Images.xcassets */; };
|
||
254BB8681B1FD16500C56DE9 /* main in Resources */ = {isa = PBXBuildFile; fileRef = 254BB8671B1FD16500C56DE9 /* main */; };
|
||
25FB30331B30FDEE0005924C /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 25FB30321B30FDEE0005924C /* assets */; };
|
||
/* End PBXBuildFile section */
|
||
|
||
/* Begin PBXFileReference section */
|
||
254BB83E1B1FD08900C56DE9 /* main.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = main.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||
254BB8421B1FD08900C56DE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||
254BB84E1B1FD08900C56DE9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||
254BB8671B1FD16500C56DE9 /* main */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = main; sourceTree = "<group>"; };
|
||
25FB30321B30FDEE0005924C /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = main/assets; sourceTree = "<group>"; };
|
||
/* End PBXFileReference section */
|
||
|
||
/* Begin PBXGroup section */
|
||
254BB8351B1FD08900C56DE9 = {
|
||
isa = PBXGroup;
|
||
children = (
|
||
25FB30321B30FDEE0005924C /* assets */,
|
||
254BB8401B1FD08900C56DE9 /* main */,
|
||
254BB83F1B1FD08900C56DE9 /* Products */,
|
||
);
|
||
sourceTree = "<group>";
|
||
usesTabs = 0;
|
||
};
|
||
254BB83F1B1FD08900C56DE9 /* Products */ = {
|
||
isa = PBXGroup;
|
||
children = (
|
||
254BB83E1B1FD08900C56DE9 /* main.app */,
|
||
);
|
||
name = Products;
|
||
sourceTree = "<group>";
|
||
};
|
||
254BB8401B1FD08900C56DE9 /* main */ = {
|
||
isa = PBXGroup;
|
||
children = (
|
||
254BB8671B1FD16500C56DE9 /* main */,
|
||
254BB84E1B1FD08900C56DE9 /* Images.xcassets */,
|
||
254BB8411B1FD08900C56DE9 /* Supporting Files */,
|
||
);
|
||
path = main;
|
||
sourceTree = "<group>";
|
||
};
|
||
254BB8411B1FD08900C56DE9 /* Supporting Files */ = {
|
||
isa = PBXGroup;
|
||
children = (
|
||
254BB8421B1FD08900C56DE9 /* Info.plist */,
|
||
);
|
||
name = "Supporting Files";
|
||
sourceTree = "<group>";
|
||
};
|
||
/* End PBXGroup section */
|
||
|
||
/* Begin PBXNativeTarget section */
|
||
254BB83D1B1FD08900C56DE9 /* main */ = {
|
||
isa = PBXNativeTarget;
|
||
buildConfigurationList = 254BB8611B1FD08900C56DE9 /* Build configuration list for PBXNativeTarget "main" */;
|
||
buildPhases = (
|
||
254BB83C1B1FD08900C56DE9 /* Resources */,
|
||
);
|
||
buildRules = (
|
||
);
|
||
dependencies = (
|
||
);
|
||
name = main;
|
||
productName = main;
|
||
productReference = 254BB83E1B1FD08900C56DE9 /* main.app */;
|
||
productType = "com.apple.product-type.application";
|
||
};
|
||
/* End PBXNativeTarget section */
|
||
|
||
/* Begin PBXProject section */
|
||
254BB8361B1FD08900C56DE9 /* Project object */ = {
|
||
isa = PBXProject;
|
||
attributes = {
|
||
LastUpgradeCheck = 0630;
|
||
ORGANIZATIONNAME = Developer;
|
||
TargetAttributes = {
|
||
254BB83D1B1FD08900C56DE9 = {
|
||
CreatedOnToolsVersion = 6.3.1;
|
||
};
|
||
};
|
||
};
|
||
buildConfigurationList = 254BB8391B1FD08900C56DE9 /* Build configuration list for PBXProject "main" */;
|
||
compatibilityVersion = "Xcode 3.2";
|
||
developmentRegion = English;
|
||
hasScannedForEncodings = 0;
|
||
knownRegions = (
|
||
en,
|
||
Base,
|
||
);
|
||
mainGroup = 254BB8351B1FD08900C56DE9;
|
||
productRefGroup = 254BB83F1B1FD08900C56DE9 /* Products */;
|
||
projectDirPath = "";
|
||
projectRoot = "";
|
||
targets = (
|
||
254BB83D1B1FD08900C56DE9 /* main */,
|
||
);
|
||
};
|
||
/* End PBXProject section */
|
||
|
||
/* Begin PBXResourcesBuildPhase section */
|
||
254BB83C1B1FD08900C56DE9 /* Resources */ = {
|
||
isa = PBXResourcesBuildPhase;
|
||
buildActionMask = 2147483647;
|
||
files = (
|
||
25FB30331B30FDEE0005924C /* assets in Resources */,
|
||
254BB8681B1FD16500C56DE9 /* main in Resources */,
|
||
254BB84F1B1FD08900C56DE9 /* Images.xcassets in Resources */,
|
||
);
|
||
runOnlyForDeploymentPostprocessing = 0;
|
||
};
|
||
/* End PBXResourcesBuildPhase section */
|
||
|
||
/* Begin XCBuildConfiguration section */
|
||
254BB8601B1FD08900C56DE9 /* Release */ = {
|
||
isa = XCBuildConfiguration;
|
||
buildSettings = {
|
||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||
CLANG_CXX_LIBRARY = "libc++";
|
||
CLANG_ENABLE_MODULES = YES;
|
||
CLANG_ENABLE_OBJC_ARC = YES;
|
||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||
CLANG_WARN_EMPTY_BODY = YES;
|
||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||
CLANG_WARN_INT_CONVERSION = YES;
|
||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||
COPY_PHASE_STRIP = NO;
|
||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||
ENABLE_NS_ASSERTIONS = NO;
|
||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||
GCC_NO_COMMON_BLOCKS = YES;
|
||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
|
||
MTL_ENABLE_DEBUG_INFO = NO;
|
||
SDKROOT = iphoneos;
|
||
TARGETED_DEVICE_FAMILY = "1,2";
|
||
VALIDATE_PRODUCT = YES;
|
||
};
|
||
name = Release;
|
||
};
|
||
254BB8631B1FD08900C56DE9 /* Release */ = {
|
||
isa = XCBuildConfiguration;
|
||
buildSettings = {
|
||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||
INFOPLIST_FILE = main/Info.plist;
|
||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||
};
|
||
name = Release;
|
||
};
|
||
/* End XCBuildConfiguration section */
|
||
|
||
/* Begin XCConfigurationList section */
|
||
254BB8391B1FD08900C56DE9 /* Build configuration list for PBXProject "main" */ = {
|
||
isa = XCConfigurationList;
|
||
buildConfigurations = (
|
||
254BB8601B1FD08900C56DE9 /* Release */,
|
||
);
|
||
defaultConfigurationIsVisible = 0;
|
||
defaultConfigurationName = Release;
|
||
};
|
||
254BB8611B1FD08900C56DE9 /* Build configuration list for PBXNativeTarget "main" */ = {
|
||
isa = XCConfigurationList;
|
||
buildConfigurations = (
|
||
254BB8631B1FD08900C56DE9 /* Release */,
|
||
);
|
||
defaultConfigurationIsVisible = 0;
|
||
defaultConfigurationName = Release;
|
||
};
|
||
/* End XCConfigurationList section */
|
||
};
|
||
rootObject = 254BB8361B1FD08900C56DE9 /* Project object */;
|
||
}
|
||
`
|
||
|
||
const contentsJSON = `{
|
||
"images" : [
|
||
{
|
||
"idiom" : "iphone",
|
||
"size" : "29x29",
|
||
"scale" : "2x"
|
||
},
|
||
{
|
||
"idiom" : "iphone",
|
||
"size" : "29x29",
|
||
"scale" : "3x"
|
||
},
|
||
{
|
||
"idiom" : "iphone",
|
||
"size" : "40x40",
|
||
"scale" : "2x"
|
||
},
|
||
{
|
||
"idiom" : "iphone",
|
||
"size" : "40x40",
|
||
"scale" : "3x"
|
||
},
|
||
{
|
||
"idiom" : "iphone",
|
||
"size" : "60x60",
|
||
"scale" : "2x"
|
||
},
|
||
{
|
||
"idiom" : "iphone",
|
||
"size" : "60x60",
|
||
"scale" : "3x"
|
||
},
|
||
{
|
||
"idiom" : "ipad",
|
||
"size" : "29x29",
|
||
"scale" : "1x"
|
||
},
|
||
{
|
||
"idiom" : "ipad",
|
||
"size" : "29x29",
|
||
"scale" : "2x"
|
||
},
|
||
{
|
||
"idiom" : "ipad",
|
||
"size" : "40x40",
|
||
"scale" : "1x"
|
||
},
|
||
{
|
||
"idiom" : "ipad",
|
||
"size" : "40x40",
|
||
"scale" : "2x"
|
||
},
|
||
{
|
||
"idiom" : "ipad",
|
||
"size" : "76x76",
|
||
"scale" : "1x"
|
||
},
|
||
{
|
||
"idiom" : "ipad",
|
||
"size" : "76x76",
|
||
"scale" : "2x"
|
||
}
|
||
],
|
||
"info" : {
|
||
"version" : 1,
|
||
"author" : "xcode"
|
||
}
|
||
}
|
||
`
|
||
|
||
// rfc1034Label sanitizes the name to be usable in a uniform type identifier.
|
||
// The sanitization is similar to xcode's rfc1034identifier macro that
|
||
// replaces illegal characters (not conforming the rfc1034 label rule) with '-'.
|
||
func rfc1034Label(name string) string {
|
||
// * Uniform type identifier:
|
||
//
|
||
// According to
|
||
// https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html
|
||
//
|
||
// A uniform type identifier is a Unicode string that usually contains characters
|
||
// in the ASCII character set. However, only a subset of the ASCII characters are
|
||
// permitted. You may use the Roman alphabet in upper and lower case (A–Z, a–z),
|
||
// the digits 0 through 9, the dot (“.”), and the hyphen (“-”). This restriction
|
||
// is based on DNS name restrictions, set forth in RFC 1035.
|
||
//
|
||
// Uniform type identifiers may also contain any of the Unicode characters greater
|
||
// than U+007F.
|
||
//
|
||
// Note: the actual implementation of xcode does not allow some unicode characters
|
||
// greater than U+007f. In this implementation, we just replace everything non
|
||
// alphanumeric with "-" like the rfc1034identifier macro.
|
||
//
|
||
// * RFC1034 Label
|
||
//
|
||
// <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
|
||
// <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
|
||
// <let-dig-hyp> ::= <let-dig> | "-"
|
||
// <let-dig> ::= <letter> | <digit>
|
||
const surrSelf = 0x10000
|
||
begin := false
|
||
|
||
var res []rune
|
||
for i, r := range name {
|
||
if r == '.' && !begin {
|
||
continue
|
||
}
|
||
begin = true
|
||
|
||
switch {
|
||
case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z':
|
||
res = append(res, r)
|
||
case '0' <= r && r <= '9':
|
||
if i == 0 {
|
||
res = append(res, '-')
|
||
} else {
|
||
res = append(res, r)
|
||
}
|
||
default:
|
||
if r < surrSelf {
|
||
res = append(res, '-')
|
||
} else {
|
||
res = append(res, '-', '-')
|
||
}
|
||
}
|
||
}
|
||
return string(res)
|
||
}
|