Skip to content

Commit dcab014

Browse files
Check if xcodeproj is required for project
When preparing iOS and merging xcconfig files if the project requires xcodeproj to be used (Cocoapods version < 1.0.0 and Xcode version >= 7.3) need to check if xcodeproj is available and fail if it's not.
1 parent 06ebd46 commit dcab014

File tree

1 file changed

+53
-35
lines changed

1 file changed

+53
-35
lines changed

lib/services/ios-project-service.ts

+53-35
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import * as helpers from "../common/helpers";
1212
import * as projectServiceBaseLib from "./platform-project-service-base";
1313
import Future = require("fibers/future");
1414
import { PlistSession } from "plist-merge-patch";
15+
import {EOL} from "os";
1516

1617
export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService {
1718
private static XCODE_PROJECT_EXT_NAME = ".xcodeproj";
@@ -41,8 +42,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
4142
private $mobileHelper: Mobile.IMobileHelper,
4243
private $pluginVariablesService: IPluginVariablesService,
4344
private $xcprojService: IXcprojService) {
44-
super($fs, $projectData, $projectDataService);
45-
}
45+
super($fs, $projectData, $projectDataService);
46+
}
4647

4748
public get platformData(): IPlatformData {
4849
let projectRoot = path.join(this.$projectData.platformsDir, "ios");
@@ -67,7 +68,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
6768
frameworkDirectoriesNames: ["Metadata", "metadataGenerator", "NativeScript", "internal"],
6869
targetedOS: ['darwin'],
6970
configurationFileName: "Info.plist",
70-
configurationFilePath: path.join(projectRoot, this.$projectData.projectName, this.$projectData.projectName+"-Info.plist"),
71+
configurationFilePath: path.join(projectRoot, this.$projectData.projectName, this.$projectData.projectName + "-Info.plist"),
7172
relativeToFrameworkConfigurationFilePath: path.join("__PROJECT_NAME__", "__PROJECT_NAME__-Info.plist"),
7273
fastLivesyncFileExtensions: [".tiff", ".tif", ".jpg", "jpeg", "gif", ".png", ".bmp", ".BMPf", ".ico", ".cur", ".xbm"] // https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImage_Class/
7374
};
@@ -77,7 +78,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
7778
return (() => {
7879
let frameworkVersion = this.getFrameworkVersion(this.platformData.frameworkPackageName).wait();
7980

80-
if(semver.lt(frameworkVersion, "1.3.0")) {
81+
if (semver.lt(frameworkVersion, "1.3.0")) {
8182
return path.join(this.platformData.projectRoot, this.$projectData.projectName, "Resources", "icons");
8283
}
8384

@@ -89,17 +90,17 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
8990
return (() => {
9091
try {
9192
this.$childProcess.exec("which xcodebuild").wait();
92-
} catch(error) {
93+
} catch (error) {
9394
this.$errors.fail("Xcode is not installed. Make sure you have Xcode installed and added to your PATH");
9495
}
9596

9697
let xcodeBuildVersion = this.$childProcess.exec("xcodebuild -version | head -n 1 | sed -e 's/Xcode //'").wait();
9798
let splitedXcodeBuildVersion = xcodeBuildVersion.split(".");
98-
if(splitedXcodeBuildVersion.length === 3) {
99+
if (splitedXcodeBuildVersion.length === 3) {
99100
xcodeBuildVersion = util.format("%s.%s", splitedXcodeBuildVersion[0], splitedXcodeBuildVersion[1]);
100101
}
101102

102-
if(helpers.versionCompare(xcodeBuildVersion, IOSProjectService.XCODEBUILD_MIN_VERSION) < 0) {
103+
if (helpers.versionCompare(xcodeBuildVersion, IOSProjectService.XCODEBUILD_MIN_VERSION) < 0) {
103104
this.$errors.fail("NativeScript can only run in Xcode version %s or greater", IOSProjectService.XCODEBUILD_MIN_VERSION);
104105
}
105106

@@ -109,13 +110,13 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
109110
public createProject(frameworkDir: string, frameworkVersion: string, pathToTemplate?: string): IFuture<void> {
110111
return (() => {
111112
this.$fs.ensureDirectoryExists(path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)).wait();
112-
if(pathToTemplate) {
113+
if (pathToTemplate) {
113114
// Copy everything except the template from the runtime
114115
this.$fs.readDirectory(frameworkDir).wait()
115116
.filter(dirName => dirName.indexOf(IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER) === -1)
116117
.forEach(dirName => shell.cp("-R", path.join(frameworkDir, dirName), this.platformData.projectRoot));
117118
shell.cp("-rf", path.join(pathToTemplate, "*"), this.platformData.projectRoot);
118-
} else if(this.$options.symlink) {
119+
} else if (this.$options.symlink) {
119120
let xcodeProjectName = util.format("%s.xcodeproj", IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER);
120121

121122
shell.cp("-R", path.join(frameworkDir, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER, "*"), path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER));
@@ -126,7 +127,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
126127
_.each(frameworkFiles, (file: string) => {
127128
this.$fs.symlink(path.join(frameworkDir, file), path.join(this.platformData.projectRoot, file)).wait();
128129
});
129-
} else {
130+
} else {
130131
shell.cp("-R", path.join(frameworkDir, "*"), this.platformData.projectRoot);
131132
}
132133
}).future<void>()();
@@ -140,7 +141,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
140141
let projectRootFilePath = path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER);
141142
// Starting with NativeScript for iOS 1.6.0, the project Info.plist file resides not in the platform project,
142143
// but in the hello-world app template as a platform specific resource.
143-
if(this.$fs.exists(path.join(projectRootFilePath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + "-Info.plist")).wait()) {
144+
if (this.$fs.exists(path.join(projectRootFilePath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + "-Info.plist")).wait()) {
144145
this.replaceFileName("-Info.plist", projectRootFilePath).wait();
145146
}
146147
this.replaceFileName("-Prefix.pch", projectRootFilePath).wait();
@@ -173,7 +174,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
173174
];
174175

175176
let xcworkspacePath = path.join(projectRoot, this.$projectData.projectName + ".xcworkspace");
176-
if(this.$fs.exists(xcworkspacePath).wait()) {
177+
if (this.$fs.exists(xcworkspacePath).wait()) {
177178
basicArgs.push("-workspace", xcworkspacePath);
178179
basicArgs.push("-scheme", this.$projectData.projectName);
179180
} else {
@@ -219,7 +220,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
219220
args.push(`PROVISIONING_PROFILE=${buildConfig.mobileProvisionIdentifier}`);
220221
}
221222

222-
this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", {cwd: this.$options, stdio: 'inherit'}).wait();
223+
this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }).wait();
223224

224225
if (buildForDevice) {
225226
let buildOutputPath = path.join(projectRoot, "build", "device");
@@ -232,7 +233,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
232233
"-o", path.join(buildOutputPath, this.$projectData.projectName + ".ipa")
233234
];
234235

235-
this.$childProcess.spawnFromEvent("xcrun", xcrunArgs, "exit", {cwd: this.$options, stdio: 'inherit'}).wait();
236+
this.$childProcess.spawnFromEvent("xcrun", xcrunArgs, "exit", { cwd: this.$options, stdio: 'inherit' }).wait();
236237
}
237238
}).future<void>()();
238239
}
@@ -257,7 +258,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
257258
architectures.push('ONLY_ACTIVE_ARCH=NO');
258259
}
259260

260-
buildConfig = buildConfig || { };
261+
buildConfig = buildConfig || {};
261262
buildConfig.architectures = architectures;
262263

263264
return this.buildProject(this.platformData.projectRoot, buildConfig);
@@ -287,7 +288,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
287288

288289
let frameworkAddOptions: xcode.Options = { customFramework: true };
289290

290-
if(isDynamic) {
291+
if (isDynamic) {
291292
frameworkAddOptions["embed"] = true;
292293
project.updateBuildProperty("IPHONEOS_DEPLOYMENT_TARGET", "8.0");
293294
this.$logger.info("The iOS Deployment Target is now 8.0 in order to support Cocoa Touch Frameworks.");
@@ -338,9 +339,9 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
338339

339340
public updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean): IFuture<boolean> {
340341
return (() => {
341-
if(!canUpdate) {
342+
if (!canUpdate) {
342343
let isUpdateConfirmed = this.$prompter.confirm(`We need to override xcodeproj file. The old one will be saved at ${this.$options.profileDir}. Are you sure?`, () => true).wait();
343-
if(isUpdateConfirmed) {
344+
if (isUpdateConfirmed) {
344345
// Copy old file to options["profile-dir"]
345346
let sourceDir = this.xcodeprojPath;
346347
let destinationDir = path.join(this.$options.profileDir, "xcodeproj");
@@ -370,7 +371,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
370371
let project = this.createPbxProj();
371372
let resources = project.pbxGroupByName("Resources");
372373

373-
if(resources) {
374+
if (resources) {
374375
let references = project.pbxFileReferenceSection();
375376

376377
let xcodeProjectImages = _.map(<any[]>resources.children, resource => this.replace(references[resource.value].name));
@@ -430,7 +431,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
430431
this.$logger.trace("Info.plist: app/App_Resources/iOS/Info.plist is missing. Upgrading the source of the project with one from the new project template. Copy " + templateInfoPlist + " to " + infoPlistPath);
431432
try {
432433
this.$fs.copyFile(templateInfoPlist, infoPlistPath).wait();
433-
} catch(e) {
434+
} catch (e) {
434435
this.$logger.trace("Copying template's Info.plist failed. " + e);
435436
}
436437
} else {
@@ -481,7 +482,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
481482
<plist version="1.0">
482483
<dict>
483484
<key>CFBundleIdentifier</key>
484-
<string>${ this.$projectData.projectId }</string>
485+
<string>${ this.$projectData.projectId}</string>
485486
</dict>
486487
</plist>`
487488
});
@@ -516,8 +517,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
516517
}
517518

518519
private replace(name: string): string {
519-
if(_.startsWith(name, '"')) {
520-
name = name.substr(1, name.length-2);
520+
if (_.startsWith(name, '"')) {
521+
name = name.substr(1, name.length - 2);
521522
}
522523

523524
return name.replace(/\\\"/g, "\"");
@@ -541,7 +542,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
541542
}
542543

543544
private savePbxProj(project: any): IFuture<void> {
544-
return this.$fs.writeFile(this.pbxProjPath, project.writeSync());
545+
return this.$fs.writeFile(this.pbxProjPath, project.writeSync());
545546
}
546547

547548
public preparePluginNativeCode(pluginData: IPluginData, opts?: any): IFuture<void> {
@@ -566,19 +567,22 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
566567

567568
public afterPrepareAllPlugins(): IFuture<void> {
568569
return (() => {
569-
if(this.$fs.exists(this.projectPodFilePath).wait()) {
570+
if (this.$fs.exists(this.projectPodFilePath).wait()) {
570571
let projectPodfileContent = this.$fs.readText(this.projectPodFilePath).wait();
571572
this.$logger.trace("Project Podfile content");
572573
this.$logger.trace(projectPodfileContent);
573574

574575
let firstPostInstallIndex = projectPodfileContent.indexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME);
575-
if(firstPostInstallIndex !== -1 && firstPostInstallIndex !== projectPodfileContent.lastIndexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME)) {
576+
if (firstPostInstallIndex !== -1 && firstPostInstallIndex !== projectPodfileContent.lastIndexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME)) {
576577
this.$logger.warn(`Podfile contains more than one post_install sections. You need to open ${this.projectPodFilePath} file and manually resolve this issue.`);
577578
}
578579

579580
let xcuserDataPath = path.join(this.xcodeprojPath, "xcuserdata");
580-
if(!this.$fs.exists(xcuserDataPath).wait()) {
581+
if (!this.$fs.exists(xcuserDataPath).wait()) {
581582
this.$logger.info("Creating project scheme...");
583+
584+
this.checkIfXcodeprojIsRequired().wait();
585+
582586
let createSchemeRubyScript = `ruby -e "require 'xcodeproj'; xcproj = Xcodeproj::Project.open('${this.$projectData.projectName}.xcodeproj'); xcproj.recreate_user_schemes; xcproj.save"`;
583587
this.$childProcess.exec(createSchemeRubyScript, { cwd: this.platformData.projectRoot }).wait();
584588
}
@@ -651,15 +655,15 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
651655
try {
652656
this.$childProcess.exec("gem which cocoapods").wait();
653657
this.$childProcess.exec("gem which xcodeproj").wait();
654-
} catch(e) {
658+
} catch (e) {
655659
this.$errors.failWithoutHelp("CocoaPods or ruby gem 'xcodeproj' is not installed. Run `sudo gem install cocoapods` and try again.");
656660
}
657661

658662
this.$xcprojService.verifyXcproj(true).wait();
659663

660664
this.$logger.info("Installing pods...");
661665
let podTool = this.$config.USE_POD_SANDBOX ? "sandbox-pod" : "pod";
662-
let childProcess = this.$childProcess.spawnFromEvent(podTool, ["install"], "close", { cwd: this.platformData.projectRoot, stdio: ['pipe', process.stdout, 'pipe'] }).wait();
666+
let childProcess = this.$childProcess.spawnFromEvent(podTool, ["install"], "close", { cwd: this.platformData.projectRoot, stdio: ['pipe', process.stdout, 'pipe'] }).wait();
663667
if (childProcess.stderr) {
664668
let warnings = childProcess.stderr.match(/(\u001b\[(?:\d*;){0,5}\d*m[\s\S]+?\u001b\[(?:\d*;){0,5}\d*m)|(\[!\].*?\n)|(.*?warning.*)/gi);
665669
_.each(warnings, (warning: string) => {
@@ -671,7 +675,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
671675
errors = errors.replace(warning, "");
672676
});
673677

674-
if(errors.trim()) {
678+
if (errors.trim()) {
675679
this.$errors.failWithoutHelp(`Pod install command failed. Error output: ${errors}`);
676680
}
677681
}
@@ -699,7 +703,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
699703
private prepareCocoapods(pluginPlatformsFolderPath: string, opts?: any): IFuture<void> {
700704
return (() => {
701705
let pluginPodFilePath = path.join(pluginPlatformsFolderPath, "Podfile");
702-
if(this.$fs.exists(pluginPodFilePath).wait()) {
706+
if (this.$fs.exists(pluginPodFilePath).wait()) {
703707
let pluginPodFileContent = this.$fs.readText(pluginPodFilePath).wait(),
704708
pluginPodFilePreparedContent = this.buildPodfileContent(pluginPodFilePath, pluginPodFileContent),
705709
projectPodFileContent = this.$fs.exists(this.projectPodFilePath).wait() ? this.$fs.readText(this.projectPodFilePath).wait() : "";
@@ -726,7 +730,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
726730
}
727731
}
728732

729-
if(opts && opts.executePodInstall && this.$fs.exists(pluginPodFilePath).wait()) {
733+
if (opts && opts.executePodInstall && this.$fs.exists(pluginPodFilePath).wait()) {
730734
this.executePodInstall().wait();
731735
}
732736
}).future<void>()();
@@ -765,12 +769,12 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
765769
private removeCocoapods(pluginPlatformsFolderPath: string): IFuture<void> {
766770
return (() => {
767771
let pluginPodFilePath = path.join(pluginPlatformsFolderPath, "Podfile");
768-
if(this.$fs.exists(pluginPodFilePath).wait() && this.$fs.exists(this.projectPodFilePath).wait()) {
772+
if (this.$fs.exists(pluginPodFilePath).wait() && this.$fs.exists(this.projectPodFilePath).wait()) {
769773
let pluginPodFileContent = this.$fs.readText(pluginPodFilePath).wait();
770774
let projectPodFileContent = this.$fs.readText(this.projectPodFilePath).wait();
771-
let contentToRemove= this.buildPodfileContent(pluginPodFilePath, pluginPodFileContent);
775+
let contentToRemove = this.buildPodfileContent(pluginPodFilePath, pluginPodFileContent);
772776
projectPodFileContent = helpers.stringReplaceAll(projectPodFileContent, contentToRemove, "");
773-
if(projectPodFileContent.trim() === `use_frameworks!${os.EOL}${os.EOL}target "${this.$projectData.projectName}" do${os.EOL}${os.EOL}end`) {
777+
if (projectPodFileContent.trim() === `use_frameworks!${os.EOL}${os.EOL}target "${this.$projectData.projectName}" do${os.EOL}${os.EOL}end`) {
774778
this.$fs.deleteFile(this.projectPodFilePath).wait();
775779
} else {
776780
this.$fs.writeFile(this.projectPodFilePath, projectPodFileContent).wait();
@@ -806,6 +810,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
806810
this.$fs.writeFile(projectFile, "").wait();
807811
}
808812

813+
this.checkIfXcodeprojIsRequired().wait();
809814
let escapedProjectFile = projectFile.replace(/'/g, "\\'"),
810815
escapedPluginFile = pluginFile.replace(/'/g, "\\'"),
811816
mergeScript = `require 'xcodeproj'; Xcodeproj::Config.new('${escapedProjectFile}').merge(Xcodeproj::Config.new('${escapedPluginFile}')).save_as(Pathname.new('${escapedProjectFile}'))`;
@@ -842,6 +847,19 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
842847
}
843848
}).future<void>()();
844849
}
850+
851+
private checkIfXcodeprojIsRequired(): IFuture<void> {
852+
return (() => {
853+
let xcprojInfo = this.$xcprojService.getXcprojInfo().wait();
854+
if (xcprojInfo.shouldUseXcproj && !xcprojInfo.xcprojAvailable) {
855+
let errorMessage = `You are using CocoaPods version ${xcprojInfo.cocoapodVer} which does not support Xcode ${xcprojInfo.xcodeVersion.major}.${xcprojInfo.xcodeVersion.minor} yet.${EOL}In order for the NativeScript CLI to be able to work correctly with this setup you need to install xcproj command line tool and add it to your PATH. Xcproj can be installed with homebrew by running $ brew install xcproj from the terminal`;
856+
857+
this.$errors.failWithoutHelp(errorMessage);
858+
859+
return true;
860+
}
861+
}).future<void>()();
862+
}
845863
}
846864

847865
$injector.register("iOSProjectService", IOSProjectService);

0 commit comments

Comments
 (0)