Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ interface IProjectTemplatesService {
defaultTemplatePath: IFuture<string>;
}

interface IPlatformProjectServiceBase {
getPluginPlatformsFolderPath(pluginData: IPluginData, platform: string): string;
}

interface IPlatformProjectService {
platformData: IPlatformData;
validate(): IFuture<void>;
Expand All @@ -30,7 +34,7 @@ interface IPlatformProjectService {
prepareProject(): IFuture<void>;
prepareAppResources(appResourcesDirectoryPath: string): IFuture<void>;
isPlatformPrepared(projectRoot: string): IFuture<boolean>;
addLibrary(platformData: IPlatformData, libraryPath: string): IFuture<void>;
addLibrary(libraryPath: string): IFuture<void>;
canUpdatePlatform(currentVersion: string, newVersion: string): IFuture<boolean>;
updatePlatform(currentVersion: string, newVersion: string): IFuture<void>;
preparePluginNativeCode(pluginData: IPluginData): IFuture<void>;
Expand Down
40 changes: 15 additions & 25 deletions lib/services/android-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import fs = require("fs");
import os = require("os");

import androidProjectPropertiesManagerLib = require("./android-project-properties-manager");
import projectServiceBaseLib = require("./platform-project-service-base");

class AndroidProjectService implements IPlatformProjectService {
class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService {
private static MIN_SUPPORTED_VERSION = 17;
private SUPPORTED_TARGETS = ["android-17", "android-18", "android-19", "android-21", "android-22"]; // forbidden for now: "android-MNC"
private static ANDROID_TARGET_PREFIX = "android";
Expand All @@ -28,13 +29,14 @@ class AndroidProjectService implements IPlatformProjectService {
constructor(private $androidEmulatorServices: Mobile.IEmulatorPlatformServices,
private $childProcess: IChildProcess,
private $errors: IErrors,
private $fs: IFileSystem,
private $hostInfo: IHostInfo,
private $injector: IInjector,
private $logger: ILogger,
private $options: IOptions,
private $projectData: IProjectData,
private $propertiesParser: IPropertiesParser,
private $options: IOptions,
private $hostInfo: IHostInfo,
private $injector: IInjector) {
$fs: IFileSystem) {
super($fs);
this._androidProjectPropertiesManagers = Object.create(null);
}

Expand Down Expand Up @@ -223,7 +225,7 @@ class AndroidProjectService implements IPlatformProjectService {
}).future<void>()();
}

public addLibrary(platformData: IPlatformData, libraryPath: string): IFuture<void> {
public addLibrary(libraryPath: string): IFuture<void> {
return (() => {
let name = path.basename(libraryPath);
let targetLibPath = this.getLibraryPath(name);
Expand All @@ -234,13 +236,13 @@ class AndroidProjectService implements IPlatformProjectService {
this.parseProjectProperties(libraryPath, targetPath).wait();

shell.cp("-f", path.join(libraryPath, "*.jar"), targetPath);
let projectLibsDir = path.join(platformData.projectRoot, "libs");
let projectLibsDir = path.join(this.platformData.projectRoot, "libs");
this.$fs.ensureDirectoryExists(projectLibsDir).wait();
shell.cp("-f", path.join(libraryPath, "*.jar"), projectLibsDir);

let libProjProp = path.join(libraryPath, "project.properties");
if (this.$fs.exists(libProjProp).wait()) {
this.updateProjectReferences(platformData.projectRoot, targetLibPath).wait();
this.updateProjectReferences(this.platformData.projectRoot, targetLibPath).wait();
}
}).future<void>()();
}
Expand All @@ -266,21 +268,21 @@ class AndroidProjectService implements IPlatformProjectService {

public preparePluginNativeCode(pluginData: IPluginData): IFuture<void> {
return (() => {
let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData);
let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME);

// Handle *.jars inside libs folder
let libsFolderPath = path.join(pluginPlatformsFolderPath, AndroidProjectService.LIBS_FOLDER_NAME);
if(this.$fs.exists(libsFolderPath).wait()) {
let libsFolderContents = this.$fs.readDirectory(libsFolderPath).wait();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to have jars not on the root level of the lib? As far as I understand from this code, we are taking only the files at the root level

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the behavior we are aiming for - all libraries have to be at root level

_(libsFolderContents)
.filter(libsFolderItem => path.extname(libsFolderItem) === ".jar")
.map(jar => this.addLibrary(this.platformData, path.join(libsFolderPath, jar)).wait())
.each(jar => this.addLibrary(path.join(libsFolderPath, jar)).wait())
.value();
}

// Handle android libraries
let librarries = this.getAllLibrariesForPlugin(pluginData).wait();
_.each(librarries, libraryName => this.addLibrary(this.platformData, path.join(pluginPlatformsFolderPath, libraryName)).wait());
_.each(librarries, libraryName => this.addLibrary(path.join(pluginPlatformsFolderPath, libraryName)).wait());
}).future<void>()();
}

Expand All @@ -299,10 +301,6 @@ class AndroidProjectService implements IPlatformProjectService {
}).future<void>()();
}

private getPluginPlatformsFolderPath(pluginData: IPluginData) {
return pluginData.pluginPlatformsFolderPath(AndroidProjectService.ANDROID_PLATFORM_NAME);
}

private getLibraryRelativePath(basePath: string, libraryPath: string): string {
return path.relative(basePath, libraryPath).split("\\").join("/");
}
Expand All @@ -320,16 +318,8 @@ class AndroidProjectService implements IPlatformProjectService {

private getAllLibrariesForPlugin(pluginData: IPluginData): IFuture<string[]> {
return (() => {
let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData);
if(pluginPlatformsFolderPath && this.$fs.exists(pluginPlatformsFolderPath).wait()) {
let platformsContents = this.$fs.readDirectory(pluginPlatformsFolderPath).wait();
return _(platformsContents)
.filter(platformItemName => platformItemName !== AndroidProjectService.LIBS_FOLDER_NAME &&
this.$fs.exists(path.join(pluginPlatformsFolderPath, platformItemName, "project.properties")).wait())
.value();
}

return [];
let filterCallback = (fileName: string, pluginPlatformsFolderPath: string) => fileName !== AndroidProjectService.LIBS_FOLDER_NAME && this.$fs.exists(path.join(pluginPlatformsFolderPath, fileName, "project.properties")).wait();
return this.getAllNativeLibrariesForPlugin(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME, filterCallback).wait();
}).future<string[]>()();
}

Expand Down
119 changes: 69 additions & 50 deletions lib/services/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,28 @@ import util = require("util");
import xcode = require("xcode");
import constants = require("./../constants");
import helpers = require("./../common/helpers");
import projectServiceBaseLib = require("./platform-project-service-base");

class IOSProjectService implements IPlatformProjectService {
class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService {
private static XCODE_PROJECT_EXT_NAME = ".xcodeproj";
private static XCODEBUILD_MIN_VERSION = "6.0";
private static IOS_PROJECT_NAME_PLACEHOLDER = "__PROJECT_NAME__";
private static IOS_PLATFORM_NAME = "ios";

private get $npmInstallationManager(): INpmInstallationManager {
return this.$injector.resolve("npmInstallationManager");
}

constructor(private $projectData: IProjectData,
private $fs: IFileSystem,
$fs: IFileSystem,
private $childProcess: IChildProcess,
private $errors: IErrors,
private $logger: ILogger,
private $iOSEmulatorServices: Mobile.IEmulatorPlatformServices,
private $options: IOptions,
private $injector: IInjector) { }
private $injector: IInjector) {
super($fs);
}

public get platformData(): IPlatformData {
var projectRoot = path.join(this.$projectData.platformsDir, "ios");
Expand Down Expand Up @@ -67,7 +71,7 @@ class IOSProjectService implements IPlatformProjectService {
var splitedXcodeBuildVersion = xcodeBuildVersion.split(".");
if(splitedXcodeBuildVersion.length === 3) {
xcodeBuildVersion = util.format("%s.%s", splitedXcodeBuildVersion[0], splitedXcodeBuildVersion[1]);
}
}

if(helpers.versionCompare(xcodeBuildVersion, IOSProjectService.XCODEBUILD_MIN_VERSION) < 0) {
this.$errors.fail("NativeScript can only run in Xcode version %s or greater", IOSProjectService.XCODEBUILD_MIN_VERSION);
Expand Down Expand Up @@ -172,23 +176,23 @@ class IOSProjectService implements IPlatformProjectService {
return this.$fs.exists(path.join(projectRoot, this.$projectData.projectName, constants.APP_FOLDER_NAME));
}

public addLibrary(platformData: IPlatformData, libraryPath: string): IFuture<void> {
return (() => {
this.validateDynamicFramework(libraryPath).wait();
var umbrellaHeader = this.getUmbrellaHeaderFromDynamicFramework(libraryPath).wait();
public addLibrary(libraryPath: string): IFuture<void> {
return (() => {
this.validateDynamicFramework(libraryPath).wait();
var umbrellaHeader = this.getUmbrellaHeaderFromDynamicFramework(libraryPath).wait();

var frameworkName = path.basename(libraryPath, path.extname(libraryPath));
var targetPath = path.join(this.$projectData.projectDir, "lib", platformData.normalizedPlatformName, frameworkName);
this.$fs.ensureDirectoryExists(targetPath).wait();
shell.cp("-R", libraryPath, targetPath);
var frameworkName = path.basename(libraryPath, path.extname(libraryPath));
var targetPath = path.join(this.$projectData.projectDir, "lib", this.platformData.normalizedPlatformName, frameworkName);
this.$fs.ensureDirectoryExists(targetPath).wait();
shell.cp("-R", libraryPath, targetPath);

let project = this.createPbxProj();
let project = this.createPbxProj();

project.addFramework(path.join(targetPath, frameworkName + ".framework"), { customFramework: true, embed: true });
project.updateBuildProperty("IPHONEOS_DEPLOYMENT_TARGET", "8.0");
this.savePbxProj(project).wait();
this.$logger.info("The iOS Deployment Target is now 8.0 in order to support Cocoa Touch Frameworks.");
}).future<void>()();
project.addFramework(path.join(targetPath, frameworkName + ".framework"), { customFramework: true, embed: true });
project.updateBuildProperty("IPHONEOS_DEPLOYMENT_TARGET", "8.0");
this.savePbxProj(project).wait();
this.$logger.info("The iOS Deployment Target is now 8.0 in order to support Cocoa Touch Frameworks.");
}).future<void>()();
}

public canUpdatePlatform(currentVersion: string, newVersion: string): IFuture<boolean> {
Expand Down Expand Up @@ -282,48 +286,63 @@ class IOSProjectService implements IPlatformProjectService {
}

public preparePluginNativeCode(pluginData: IPluginData): IFuture<void> {
return Future.fromResult();
return (() => {
let pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
_.each(this.getAllDynamicFrameworksForPlugin(pluginData).wait(), fileName => this.addLibrary(path.join(pluginPlatformsFolderPath, fileName)).wait());
}).future<void>()();
}

public removePluginNativeCode(pluginData: IPluginData): IFuture<void> {
return Future.fromResult();
return (() => {
let pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
let project = this.createPbxProj();

_.each(this.getAllDynamicFrameworksForPlugin(pluginData).wait(), fileName => project.removeFramework(path.join(pluginPlatformsFolderPath, fileName + ".framework"), { customFramework: true, embed: true }));

this.savePbxProj(project).wait();
}).future<void>()();
}

private getAllDynamicFrameworksForPlugin(pluginData: IPluginData): IFuture<string[]> {
let filterCallback = (fileName: string, pluginPlatformsFolderPath: string) => path.extname(fileName) === ".framework";
return this.getAllNativeLibrariesForPlugin(pluginData, IOSProjectService.IOS_PLATFORM_NAME, filterCallback);
}

private buildPathToXcodeProjectFile(version: string): string {
return path.join(this.$npmInstallationManager.getCachedPackagePath(this.platformData.frameworkPackageName, version), constants.PROJECT_FRAMEWORK_FOLDER_NAME, util.format("%s.xcodeproj", IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER), "project.pbxproj");
}

private validateDynamicFramework(libraryPath: string): IFuture<void> {
return (() => {
var infoPlistPath = path.join(libraryPath, "Info.plist");
if (!this.$fs.exists(infoPlistPath).wait()) {
this.$errors.failWithoutHelp("The bundle at %s does not contain an Info.plist file.", libraryPath);
}

var packageType = this.$childProcess.exec(`/usr/libexec/PlistBuddy -c "Print :CFBundlePackageType" "${infoPlistPath}"`).wait().trim();
if (packageType !== "FMWK") {
this.$errors.failWithoutHelp("The bundle at %s does not appear to be a dynamic framework.", libraryPath);
}
}).future<void>()();
}

private getUmbrellaHeaderFromDynamicFramework(libraryPath: string): IFuture<string> {
return (() => {
var modulemapPath = path.join(libraryPath, "Modules", "module.modulemap");
if (!this.$fs.exists(modulemapPath).wait()) {
private validateDynamicFramework(libraryPath: string): IFuture<void> {
return (() => {
var infoPlistPath = path.join(libraryPath, "Info.plist");
if (!this.$fs.exists(infoPlistPath).wait()) {
this.$errors.failWithoutHelp("The bundle at %s does not contain an Info.plist file.", libraryPath);
}

var packageType = this.$childProcess.exec(`/usr/libexec/PlistBuddy -c "Print :CFBundlePackageType" "${infoPlistPath}"`).wait().trim();
if (packageType !== "FMWK") {
this.$errors.failWithoutHelp("The bundle at %s does not appear to be a dynamic framework.", libraryPath);
}
}).future<void>()();
}

private getUmbrellaHeaderFromDynamicFramework(libraryPath: string): IFuture<string> {
return (() => {
var modulemapPath = path.join(libraryPath, "Modules", "module.modulemap");
if (!this.$fs.exists(modulemapPath).wait()) {
this.$errors.failWithoutHelp("The framework at %s does not contain a module.modulemap file.", modulemapPath);
}

var modulemap = this.$fs.readText(modulemapPath).wait();
var umbrellaRegex = /umbrella header "(.+\.h)"/g;
var match = umbrellaRegex.exec(modulemap);
if (!match) {
this.$errors.failWithoutHelp("The modulemap at %s does not specify an umbrella header.", modulemapPath);
}

return match[1];
}).future<string>()();
}
}

var modulemap = this.$fs.readText(modulemapPath).wait();
var umbrellaRegex = /umbrella header "(.+\.h)"/g;
var match = umbrellaRegex.exec(modulemap);
if (!match) {
this.$errors.failWithoutHelp("The modulemap at %s does not specify an umbrella header.", modulemapPath);
}

return match[1];
}).future<string>()();
}

private replaceFileContent(file: string): IFuture<void> {
return (() => {
Expand Down
26 changes: 26 additions & 0 deletions lib/services/platform-project-service-base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
///<reference path="../.d.ts"/>
"use strict";
import path = require("path");

export class PlatformProjectServiceBase implements IPlatformProjectServiceBase {
constructor(protected $fs: IFileSystem) { }

public getPluginPlatformsFolderPath(pluginData: IPluginData, platform: string) {
return pluginData.pluginPlatformsFolderPath(platform);
}

public getAllNativeLibrariesForPlugin(pluginData: IPluginData, platform: string, filter: (fileName: string, pluginPlatformsFolderPath: string) => boolean): IFuture<string[]> {
return (() => {
let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, platform),
nativeLibraries: string[] = [];
if(pluginPlatformsFolderPath && this.$fs.exists(pluginPlatformsFolderPath).wait()) {
let platformsContents = this.$fs.readDirectory(pluginPlatformsFolderPath).wait();
nativeLibraries = _(platformsContents)
.filter(platformItemName => filter(platformItemName, pluginPlatformsFolderPath))
.value();
}

return nativeLibraries;
}).future<string[]>()();
}
}
2 changes: 1 addition & 1 deletion lib/services/platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ export class PlatformService implements IPlatformService {
this.$errors.failWithoutHelp("The path %s does not exist", libraryPath);
} else {
var platformData = this.$platformsData.getPlatformData(platform);
platformData.platformProjectService.addLibrary(platformData, libraryPath).wait();
platformData.platformProjectService.addLibrary(libraryPath).wait();
}
}).future<void>()();
}
Expand Down
2 changes: 1 addition & 1 deletion test/stubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export class PlatformProjectServiceStub implements IPlatformProjectService {
isPlatformPrepared(projectRoot: string): IFuture<boolean> {
return Future.fromResult(false);
}
addLibrary(platformData: IPlatformData, libraryPath: string): IFuture<void> {
addLibrary(libraryPath: string): IFuture<void> {
return Future.fromResult();
}
getDebugOnDeviceSetup(): Mobile.IDebugOnDeviceSetup {
Expand Down