Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2012 Trillian Mobile AB
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
  */
 package org.robovm.compiler.target.ios;
 
 import java.io.File;
 import java.util.List;
 import java.util.Map;
 
 
 

Author(s):
niklas
 
 public class IOSTarget extends AbstractTarget {
 
     private Arch arch;
     private SDK sdk;
     private File infoPList = null;
     private NSDictionary infoPListDict = null;
     private File resourceRulesPList;
     private File entitlementsPList;
     private SigningIdentity signIdentity;
     
     public IOSTarget() {
     }
     
     @Override
     public Arch getArch() {
         return ;
     }
     
     @Override
         if ( == .) {
             return new IOSSimulatorLaunchParameters();
         }
         return new IOSDeviceLaunchParameters();
     }
     
     public List<SDKgetSDKs() {
         if ( == .) {
             return SDK.listSimulatorSDKs();
         } else {
            return SDK.listDeviceSDKs();
        }
    }
    @Override
    protected Launcher createLauncher(LaunchParameters launchParametersthrows IOException {
        if ( == .) {
            return createIOSSimLauncher(launchParameters);
        } else {
            return createIOSDevLauncher(launchParameters);
        }
    }
    
    private Launcher createIOSSimLauncher(LaunchParameters launchParameters)
            throws IOException {
        File dir = getAppDir();
        
        String iosSimPath = new File(.getHome().getBinDir(), "ios-sim").getAbsolutePath();
        
        List<Objectargs = new ArrayList<Object>();
        args.add("launch");
        args.add(dir);
        args.add("--timeout");
        args.add("90");
        args.add("--unbuffered");
        if (((IOSSimulatorLaunchParameterslaunchParameters).getSdk() != null) {
            args.add("--sdk");
            args.add(((IOSSimulatorLaunchParameterslaunchParameters).getSdk());
        }
        args.addAll(((IOSSimulatorLaunchParameterslaunchParameters).getFamily().getIosSimArgs());
        if (launchParameters.getStdoutFifo() != null) {
            args.add("--stdout");
            args.add(launchParameters.getStdoutFifo());
        }
        if (launchParameters.getStderrFifo() != null) {
            args.add("--stderr");
            args.add(launchParameters.getStderrFifo());
        }
        if (!launchParameters.getArguments().isEmpty()) {
            args.add("--args");
            args.addAll(launchParameters.getArguments());
        }
        
        File xcodePath = new File(ToolchainUtil.findXcodePath());
        Map<StringStringenv = Collections.singletonMap("DEVELOPER_DIR"xcodePath.getAbsolutePath());
        return new Executor(.getLogger(), iosSimPath)
            .args(args)
            .wd(launchParameters.getWorkingDirectory())
            .inheritEnv(false)
            .env(env);
    }
    
    private Launcher createIOSDevLauncher(LaunchParameters launchParameters)
            throws IOException {
        
        String deviceId = ((IOSDeviceLaunchParameterslaunchParameters).getDeviceId();
        if (deviceId == null) {
            String[] udids = IDevice.listUdids();
            if (udids.length == 0) {
                throw new RuntimeException("No devices connected");
            }
            if (udids.length > 1) {
                .getLogger().warn("More than 1 device connected (%s). " 
                        + "Using %s.", Arrays.asList(udids), udids[0]);
            }
            deviceId = udids[0];
        }
        IDevice device = new IDevice(deviceId);
        
        OutputStream out = null;
        if (launchParameters.getStdoutFifo() != null) {
            out = new OpenOnWriteFileOutputStream(launchParameters.getStdoutFifo());
        } else {
            out = .;
        }
        
        Map<StringStringenv = launchParameters.getEnvironment();
        if (env == null) {
            env = Collections.emptyMap();
        }
        
        AppLauncher launcher = new AppLauncher(devicegetAppDir()) {
            protected void log(String sObject ... args) {
                .getLogger().debug(sargs);
            }
        }
            .stdout(out)
            .closeOutOnExit(true)
            .args(launchParameters.getArguments().toArray(new String[0]))
            .env(env)
            .xcodePath(ToolchainUtil.findXcodePath())
            .uploadProgressCallback(new UploadProgressCallback() {
                boolean first = true;
                public void success() {
                    .getLogger().debug("[100%%] Upload complete");
                }
                public void progress(File pathint percentComplete) {
                    if () {
                        .getLogger().debug("[  0%%] Beginning upload...");
                    }
                     = false;
                    .getLogger().debug("[%3d%%] Uploading %s..."percentCompletepath);
                }
                public void error(String message) {
                }
            })
            .installStatusCallback(new StatusCallback() {
                boolean first = true;
                public void success() {
                    .getLogger().debug("[100%%] Install complete");
                }
                public void progress(String statusint percentComplete) {
                    if () {
                        .getLogger().debug("[  0%%] Beginning installation...");
                    }
                     = false;
                    .getLogger().debug("[%3d%%] %s"percentCompletestatus);
                }
                public void error(String message) {
                }
            });
        
        return new AppLauncherProcess(.getLogger(), launcherlaunchParameters);
    }
    @Override
    protected void doBuild(File outFileList<StringccArgs,
            List<FileobjectFilesList<StringlibArgs)
            throws IOException {
        if ( == .) {
            ccArgs.add("-miphoneos-version-min=5.0");
        } else {
            ccArgs.add("-mios-simulator-version-min=5.0");
        }
        ccArgs.add("-isysroot");
        ccArgs.add(.getRoot().getAbsolutePath());
        super.doBuild(outFileccArgsobjectFileslibArgs);
    }
    protected void prepareInstall(File installDirthrows IOException {
        createInfoPList(installDir);
        generateDsym(installDirgetExecutable());
        if ( == .) {
            strip(installDirgetExecutable());
            copyResourcesPList(installDir);
            if (.isIosSkipSigning()) {
                .getLogger().warn("Skiping code signing. The resulting app will "
                        + "be unsigned and will not run on unjailbroken devices");
                ldid(installDir);
            } else {
                // Copy the provisioning profile
                copyProvisioningProfile(installDir);
                boolean getTaskAllow = .getType() == .;
                codesign(getOrCreateEntitlementsPList(getTaskAllow), installDir);
                // For some odd reason there needs to be a symbolic link in the root of
                // the app bundle named CodeResources pointing at _CodeSignature/CodeResources
                new Executor(.getLogger(), "ln")
                    .args("-f""-s""_CodeSignature/CodeResources"new File(installDir"CodeResources"))
                    .exec();
            }
        }
    }
    private void copyProvisioningProfile(ProvisioningProfile profileFile destDirthrows IOException {
        .getLogger().debug("Copying %s provisioning profile: %s (%s)",
                profile.getType(),
                profile.getName(), 
                profile.getEntitlements().objectForKey("application-identifier"));
        FileUtils.copyFile(profile.getFile(), new File(destDir"embedded.mobileprovision"));
    }
    
    protected void prepareLaunch(File appDirthrows IOException {
        super.doInstall(appDirgetExecutable());
        createInfoPList(appDir);
        generateDsym(appDirgetExecutable());
        if ( == .) {
            copyResourcesPList(appDir);
            if (.isIosSkipSigning()) {
                .getLogger().warn("Skiping code signing. The resulting app will "
                        + "be unsigned and will not run on unjailbroken devices");
                ldid(getOrCreateEntitlementsPList(true), appDir);
            } else {
                copyProvisioningProfile(appDir);
                codesign(getOrCreateEntitlementsPList(true), appDir);
            }
        }
    }
    
    private void codesign(SigningIdentity identityFile entitlementsPListFile appDirthrows IOException {
        .getLogger().debug("Code signing using identity '%s' with fingerprint %s"identity.getName(), 
                identity.getFingerprint());
        List<Objectargs = new ArrayList<Object>();
        args.add("-f");
        args.add("-s");
        args.add(identity.getFingerprint());
        if (entitlementsPList != null) {
            args.add("--entitlements");
            args.add(entitlementsPList);
        }
        args.add(appDir);
        new Executor(.getLogger(), "codesign")
            .addEnv("CODESIGN_ALLOCATE", ToolchainUtil.findXcodeCommand("codesign_allocate""iphoneos"))
            .args(args)
            .exec();
    }
    private void ldid(File entitlementsPListFile appDirthrows IOException {
        File executableFile = new File(appDirgetExecutable());
        .getLogger().debug("Pseudo-signing %s"executableFile.getAbsolutePath());
        List<Objectargs = new ArrayList<Object>();
        if (entitlementsPList != null) {
            args.add("-S" + entitlementsPList.getAbsolutePath());
        } else {
            args.add("-S");
        }
        args.add(executableFile);
        new Executor(.getLogger(), new File(.getHome().getBinDir(), "ldid"))
            .args(args)
            .exec();
    }
    private void copyResourcesPList(File destDirthrows IOException {
        File destFile = new File(destDir"ResourceRules.plist");
        if ( != null) {
            FileUtils.copyFile(destFile);
        } else {
            FileUtils.copyURLToFile(getClass().getResource("/ResourceRules.plist"), destFile);
        }
    }
    
    private File getOrCreateEntitlementsPList(boolean getTaskAllowthrows IOException {
        try {
            File destFile = new File(.getTmpDir(), "Entitlements.plist");
            NSDictionary dict = null;
            if ( != null) {
                dict = (NSDictionary) PropertyListParser.parse();
            } else {
                dict = (NSDictionary) PropertyListParser.parse(IOUtils.toByteArray(getClass().getResourceAsStream("/Entitlements.plist")));
            }
            if ( != null) {
                NSDictionary profileEntitlements = .getEntitlements();
                for (String key : profileEntitlements.allKeys()) {
                    if (dict.objectForKey(key) == null) {
                        dict.put(keyprofileEntitlements.objectForKey(key));
                    }
                }
                dict.put("application-identifier".getAppIdPrefix() + "." + getBundleId());
            }
            dict.put("get-task-allow"getTaskAllow);
            PropertyListParser.saveAsXML(dictdestFile);
            return destFile;
        } catch (IOException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    private void generateDsym(File dirString executablethrows IOException {
        File dsymDir = new File(dir.getParentFile(), dir.getName() + ".dSYM");
        FileUtils.deleteDirectory(dsymDir);
        new Executor(.getLogger(), "xcrun")
            .args("dsymutil""-o"dsymDirnew File(direxecutable))
            .exec();
    }
    private void strip(File dirString executablethrows IOException {
        File exportedSymbolsFile = new File(.getTmpDir(), "exported_symbols");
        new Executor(.getLogger(), "xcrun")
            .args("strip""-s"exportedSymbolsFilenew File(direxecutable))
            .exec();
    }
    
    @Override
    protected void doInstall(File installDirString executablethrows IOException {
        super.doInstall(installDirgetExecutable());
        prepareInstall(installDir);
    }
    
    @Override
    protected Process doLaunch(LaunchParameters launchParametersthrows IOException {
        prepareLaunch(getAppDir());
        return super.doLaunch(launchParameters);
    }
    public void createIpa() throws IOException {
        .getLogger().debug("Creating IPA in %s".getInstallDir());
        .getInstallDir().mkdirs();
        File tmpDir = new File(.getInstallDir(), getExecutable() + ".app");
        FileUtils.deleteDirectory(tmpDir);
        tmpDir.mkdirs();
        super.doInstall(tmpDirgetExecutable());
        prepareInstall(tmpDir);
        ToolchainUtil.packageApplication(tmpDirnew File(.getInstallDir(), getExecutable() + ".ipa"));
    }
    
    @Override
    protected void copyFile(Resource resourceFile fileFile destDir)
            throws IOException {
        
        if ( == . && !resource.isSkipPngCrush() 
                && file.getName().toLowerCase().endsWith(".png")) {
            
            destDir.mkdirs();
            File outFile = new File(destDirfile.getName());
            ToolchainUtil.pngcrush(fileoutFile);
        } else if (file.getName().toLowerCase().endsWith(".strings")) {
            destDir.mkdirs();
            File outFile = new File(destDirfile.getName());
            ToolchainUtil.compileStrings(fileoutFile);
        } else {
            super.copyFile(resourcefiledestDir);
        }
    }
    
    protected File getAppDir() {
        File dir = null;
        if (!.isSkipInstall()) {
            dir = .getInstallDir();
        } else {
            dir = new File(.getTmpDir(), getExecutable() + ".app");
            dir.mkdirs();
        }
        return dir;
    }
    
    protected String getExecutable() {
        if ( != null) {
            NSObject bundleExecutable = .objectForKey("CFBundleExecutable");
            if (bundleExecutable != null) {
                return bundleExecutable.toString();
            }
        }
        return .getExecutableName();
    }
    protected String getBundleId() {
        if ( != null) {
            NSObject bundleIdentifier = .objectForKey("CFBundleIdentifier");
            if (bundleIdentifier != null) {
                return bundleIdentifier.toString();
            }
        }
        return .getMainClass() != null ? .getMainClass() : .getExecutableName();
    }
    
    private void putIfAbsent(NSDictionary dictString keyString value) {
        if (dict.objectForKey(key) == null) {
            dict.put(keyvalue);
        }
    }
    protected void customizeInfoPList(NSDictionary dict) {
        if ( == .) {
            dict.put("CFBundleSupportedPlatforms"new NSArray(new NSString("iPhoneSimulator")));
        } else {
            dict.put("CFBundleResourceSpecification""ResourceRules.plist");
            dict.put("CFBundleSupportedPlatforms"new NSArray(new NSString("iPhoneOS")));
            dict.put("DTPlatformVersion".getPlatformVersion());
            dict.put("DTPlatformBuild".getPlatformBuild());
            dict.put("DTSDKBuild".getBuild());
            // Validation fails without these. Let's pretend the app was built with Xcode 5.0.2
            putIfAbsent(dict"DTXcode""0502");
            putIfAbsent(dict"DTXcodeBuild""5A3005");
        }
    }
    
    protected void createInfoPList(File dirthrows IOException {
        NSDictionary dict = new NSDictionary();
        if ( != null) {
            for (String key : .allKeys()) {
                dict.put(key.objectForKey(key));
            }
        } else {
            dict.put("CFBundleVersion""1.0");
            dict.put("CFBundleExecutable".getExecutableName());
            dict.put("CFBundleName".getExecutableName());
            dict.put("CFBundleIdentifier"getBundleId());
            dict.put("CFBundlePackageType""APPL");
            dict.put("LSRequiresIPhoneOS"true);
            NSObject supportedDeviceFamilies = .getDefaultProperty("SUPPORTED_DEVICE_FAMILIES");
            if (supportedDeviceFamilies != null) {
                // SUPPORTED_DEVICE_FAMILIES is either a NSString of comma separated numbers
                // or an NSArray with NSStrings. UIDeviceFamily values should be NSNumbers.
                NSArray families = null;
                if (supportedDeviceFamilies instanceof NSString) {
                    NSString defFamilies = (NSStringsupportedDeviceFamilies;
                    String[] parts = defFamilies.toString().split(",");
                    families = new NSArray(parts.length);
                    for (int i = 0; i < families.count(); i++) {
                        families.setValue(inew NSNumber(parts[i].trim()));
                    }
                } else {
                    NSArray defFamilies = (NSArraysupportedDeviceFamilies;
                    families = new NSArray(defFamilies.count());
                    for (int i = 0; i < families.count(); i++) {
                        families.setValue(inew NSNumber(defFamilies.objectAtIndex(i).toString()));
                    }
                }
                dict.put("UIDeviceFamily"families);
            }
            dict.put("UISupportedInterfaceOrientations"new NSArray(
                    new NSString("UIInterfaceOrientationPortrait"),
                    new NSString("UIInterfaceOrientationLandscapeLeft"),
                    new NSString("UIInterfaceOrientationLandscapeRight"),
                    new NSString("UIInterfaceOrientationPortraitUpsideDown")
            ));
            dict.put("UISupportedInterfaceOrientations~ipad"new NSArray(
                    new NSString("UIInterfaceOrientationPortrait"),
                    new NSString("UIInterfaceOrientationLandscapeLeft"),
                    new NSString("UIInterfaceOrientationLandscapeRight"),
                    new NSString("UIInterfaceOrientationPortraitUpsideDown")
            ));
            dict.put("UIRequiredDeviceCapabilities"new NSArray(new NSString("armv7")));
        }
        dict.put("DTPlatformName".getPlatformName());
        dict.put("DTSDKName".getCanonicalName());
        if (dict.objectForKey("MinimumOSVersion") == null) {
            // This is required
            dict.put("MinimumOSVersion""5.0");
        }
        
        customizeInfoPList(dict);
        File tmpInfoPlist = new File(.getTmpDir(), "Info.plist");
        PropertyListParser.saveAsBinary(dicttmpInfoPlist);
        
        .getLogger().debug("Installing Info.plist to %s"dir);
        FileUtils.copyFile(tmpInfoPlistnew File(dirtmpInfoPlist.getName()));
    }
    
    public void init(Config config) {
        super.init(config);
        
        if (config.getArch() == null) {
             = .;
        } else {
            if (config.getArch() != . && config.getArch() != .) {
                throw new IllegalArgumentException("Arch '" + config.getArch() 
                        + "' is unsupported for iOS target");
            }
             = config.getArch();
        }
        if ( == .) {
            if (!config.isIosSkipSigning()) {
                 = config.getIosSignIdentity();
                if ( == null) {
                     = SigningIdentity.find(SigningIdentity.list(), "iPhone Developer");
                }
            }
        }
        
         = config.getIosInfoPList();
        if ( != null) {
            try {
                 = (NSDictionaryparsePropertyList(config.getProperties());
            } catch (Throwable t) {
                throw new IllegalArgumentException("Failed to parse Info.plist XML file: " + t);
            }
        }
        if ( == .) {
            if (!config.isIosSkipSigning()) {
                 = config.getIosProvisioningProfile();
                if ( == null) {
                    NSString bundleId =  != null ? (NSString.objectForKey("CFBundleIdentifier") : null;
                    if (bundleId == null) {
                        bundleId = new NSString("*");
                    }
                     = ProvisioningProfile.find(ProvisioningProfile.list(), bundleId.toString());
                }
            }
        }
        String sdkVersion = config.getIosSdkVersion();
        List<SDKsdks = getSDKs();
        if (sdkVersion == null) {
            if (sdks.isEmpty()) {
                throw new IllegalArgumentException("No " + ( == . ? "device" : "simulator") + " SDKs installed");
            }
            Collections.sort(sdks);
            this. = sdks.get(sdks.size() - 1);
        } else {
            for (SDK sdk : sdks) {
                if (sdk.getVersion().equals(sdkVersion)) {
                    this. = sdk;
                    break;
                }
            }
            if ( == null) {
                throw new IllegalArgumentException("No SDK found matching version string " + sdkVersion);
            }
        }
    }
    @Override
    public OS getOs() {
        return .;
    }
    @Override
    public boolean canLaunchInPlace() {
        return false;
    }
    
    private final static Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{([^}]+)\\}");
    
    static void replacePropertyRefs(Node nodeProperties props) {
        if (node instanceof Text) {
            Text el = (Textnode;
            String value = el.getNodeValue();
            if (value != null && value.trim().length() > 0) {
                Matcher matcher = .matcher(value);
                StringBuilder sb = new StringBuilder();
                int pos = 0;
                while (matcher.find()) {
                    if (pos < matcher.start()) {
                        sb.append(value.substring(posmatcher.start()));
                    }
                    String key = matcher.group(1);
                    sb.append(props.getProperty(keymatcher.group()));
                    pos = matcher.end();
                }
                if (pos < value.length()) {
                    sb.append(value.substring(pos));
                }
                el.setNodeValue(sb.toString());
            }
        }
        NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            replacePropertyRefs(children.item(i), props);
        }
    }
    
    static NSObject parsePropertyList(File fileProperties propsthrows Exception {
        Properties allProps = new Properties(System.getProperties());
        allProps.putAll(props);
        
        Method getDocBuilder = XMLPropertyListParser.class.getDeclaredMethod("getDocBuilder");
        getDocBuilder.setAccessible(true);
        Method parseDocument = XMLPropertyListParser.class.getDeclaredMethod("parseDocument"Document.class);
        parseDocument.setAccessible(true);
        DocumentBuilder docBuilder = (DocumentBuildergetDocBuilder.invoke(null);
        Document doc = docBuilder.parse(file);
        replacePropertyRefs(docallProps);
        return (NSObjectparseDocument.invoke(nulldoc);
    }
New to GrepCode? Check out our FAQ X