@@ -5,19 +5,21 @@ import Future = require("fibers/future");
55import path = require( "path" ) ;
66import shell = require( "shelljs" ) ;
77import util = require( "util" ) ;
8+ import xcode = require( "xcode" ) ;
89import constants = require( "./../constants" ) ;
910import helpers = require( "./../common/helpers" ) ;
1011import options = require( "../common/options" ) ;
1112
1213class IOSProjectService implements IPlatformProjectService {
1314 private static XCODE_PROJECT_EXT_NAME = ".xcodeproj" ;
14- private static XCODEBUILD_MIN_VERSION = "5 .0" ;
15+ private static XCODEBUILD_MIN_VERSION = "6 .0" ;
1516 private static IOS_PROJECT_NAME_PLACEHOLDER = "__PROJECT_NAME__" ;
1617
1718 constructor ( private $projectData : IProjectData ,
1819 private $fs : IFileSystem ,
1920 private $childProcess : IChildProcess ,
2021 private $errors : IErrors ,
22+ private $logger : ILogger ,
2123 private $iOSEmulatorServices : Mobile . IEmulatorPlatformServices ) { }
2224
2325 public get platformData ( ) : IPlatformData {
@@ -170,14 +172,86 @@ class IOSProjectService implements IPlatformProjectService {
170172 }
171173
172174 public addLibrary ( platformData : IPlatformData , libraryPath : string ) : IFuture < void > {
173- var name = path . basename ( libraryPath ) ;
174- var targetPath = path . join ( this . $projectData . projectDir , "lib" , platformData . normalizedPlatformName , path . basename ( name , path . extname ( name ) ) ) ;
175- this . $fs . ensureDirectoryExists ( targetPath ) . wait ( ) ;
175+ return ( ( ) => {
176+ this . validateDynamicFramework ( libraryPath ) . wait ( ) ;
177+ var umbrellaHeader = this . getUmbrellaHeaderFromDynamicFramework ( libraryPath ) . wait ( ) ;
178+
179+ var frameworkName = path . basename ( libraryPath , path . extname ( libraryPath ) ) ;
180+ var targetPath = path . join ( this . $projectData . projectDir , "lib" , platformData . normalizedPlatformName , frameworkName ) ;
181+ this . $fs . ensureDirectoryExists ( targetPath ) . wait ( ) ;
182+ shell . cp ( "-R" , libraryPath , targetPath ) ;
183+
184+ this . generateFrameworkMetadata ( platformData . projectRoot , targetPath , frameworkName , umbrellaHeader ) . wait ( ) ;
185+
186+ var pbxProjPath = path . join ( platformData . projectRoot , this . $projectData . projectName + ".xcodeproj" , "project.pbxproj" ) ;
187+ var project = new xcode . project ( pbxProjPath ) ;
188+ project . parseSync ( ) ;
189+
190+ project . addFramework ( path . join ( targetPath , frameworkName + ".framework" ) , { customFramework : true , embed : true } ) ;
191+ project . updateBuildProperty ( "IPHONEOS_DEPLOYMENT_TARGET" , "8.0" ) ;
192+ this . $fs . writeFile ( pbxProjPath , project . writeSync ( ) ) . wait ( ) ;
193+ this . $logger . info ( "The iOS Deployment Target is now 8.0 in order to support Cocoa Touch Frameworks." ) ;
194+ } ) . future < void > ( ) ( ) ;
195+ }
196+
197+ private validateDynamicFramework ( libraryPath : string ) : IFuture < void > {
198+ return ( ( ) => {
199+ var infoPlistPath = path . join ( libraryPath , "Info.plist" ) ;
200+ if ( ! this . $fs . exists ( infoPlistPath ) . wait ( ) ) {
201+ this . $errors . failWithoutHelp ( "The bundle at %s does not contain an Info.plist file." , libraryPath ) ;
202+ }
203+
204+ var packageType = this . $childProcess . exec ( util . format ( "/usr/libexec/PlistBuddy -c \"Print :CFBundlePackageType\" %s" , infoPlistPath ) ) . wait ( ) . trim ( ) ;
205+ if ( packageType != "FMWK" ) {
206+ this . $errors . failWithoutHelp ( "The bundle at %s does not appear to be a dynamic framework." , libraryPath ) ;
207+ }
208+ } ) . future < void > ( ) ( ) ;
209+ }
210+
211+ private getUmbrellaHeaderFromDynamicFramework ( libraryPath : string ) : IFuture < string > {
212+ return ( ( ) => {
213+ var modulemapPath = path . join ( libraryPath , "Modules" , "module.modulemap" ) ;
214+ if ( ! this . $fs . exists ( modulemapPath ) . wait ( ) ) {
215+ this . $errors . failWithoutHelp ( "The framework at %s does not contain a module.modulemap file." ) ;
216+ }
217+
218+ var modulemap = this . $fs . readText ( modulemapPath ) . wait ( ) ;
219+ var umbrellaRegex = / u m b r e l l a h e a d e r " ( .+ \. h ) " / g;
220+ var match = umbrellaRegex . exec ( modulemap ) ;
221+ if ( ! match ) {
222+ this . $errors . failWithoutHelp ( "The modulemap at %s does not specify an umbrella header." , modulemapPath ) ;
223+ }
224+
225+ return match [ 1 ] ;
226+ } ) . future < string > ( ) ( ) ;
227+ }
228+
229+ private generateFrameworkMetadata ( projectRoot : string , frameworkDir : string , frameworkName : string , umbrellaHeader : string ) : IFuture < void > {
230+ return ( ( ) => {
231+ if ( ! this . $fs . exists ( "/usr/local/lib/libmonoboehm-2.0.1.dylib" ) . wait ( ) ) {
232+ this . $errors . failWithoutHelp ( "NativeScript needs Mono 3.10 or newer installed in /usr/local" ) ;
233+ }
234+
235+ var yamlOut = path . join ( frameworkDir , "Metadata" ) ;
236+ this . $fs . createDirectory ( yamlOut ) . wait ( ) ;
237+
238+ var tempHeader = path . join ( yamlOut , "Metadata.h" ) ;
239+ this . $fs . writeFile ( tempHeader , util . format ( "#import <%s/%s>" , frameworkName , umbrellaHeader ) ) . wait ( ) ;
240+
241+ this . $logger . info ( "Generating metadata for %s.framework. This can take a minute." , frameworkName ) ;
242+ var sdkPath = this . $childProcess . exec ( "xcrun -sdk iphoneos --show-sdk-path" ) . wait ( ) . trim ( ) ;
243+ var generatorExecOptions = {
244+ env : {
245+ DYLD_FALLBACK_LIBRARY_PATH : this . $childProcess . exec ( "xcode-select -p" ) . wait ( ) . trim ( ) + "/Toolchains/XcodeDefault.xctoolchain/usr/lib"
246+ }
247+ } ;
248+ this . $childProcess . exec ( util . format ( "%s/Metadata/MetadataGenerator -s %s -u %s -o %s -cflags=\"-F%s\"" , projectRoot , sdkPath , tempHeader , yamlOut , frameworkDir ) , generatorExecOptions ) . wait ( ) ;
176249
177- shell . cp ( "-R" , libraryPath , targetPath ) ;
250+ this . $fs . copyFile ( path . join ( yamlOut , "Metadata-armv7" , frameworkName + ".yaml" ) , path . join ( projectRoot , "Metadata" , "Metadata-armv7" , frameworkName + ".yaml" ) ) . wait ( ) ;
251+ this . $fs . copyFile ( path . join ( yamlOut , "Metadata-arm64" , frameworkName + ".yaml" ) , path . join ( projectRoot , "Metadata" , "Metadata-arm64" , frameworkName + ".yaml" ) ) . wait ( ) ;
178252
179- this . $errors . fail ( "Implement me!" ) ;
180- return Future . fromResult ( ) ;
253+ this . $fs . deleteDirectory ( yamlOut ) . wait ( ) ;
254+ } ) . future < void > ( ) ( ) ;
181255 }
182256
183257 private replaceFileContent ( file : string ) : IFuture < void > {
0 commit comments