diff --git a/.gitignore b/.gitignore index 651665f9fd..52dd0cd6a2 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ results scratch/ .idea/ .settings/ +.vscode/ test-reports.xml npm-debug.log diff --git a/CHANGELOG.md b/CHANGELOG.md index d585382336..781749ce76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,29 @@ NativeScript CLI Changelog ================ +1.3.0 (2015, September 16) +== + +### New +* [Implemented #390](https://github.com/NativeScript/nativescript-cli/issues/390): Support tns_modules from npm. +* [Implemented #686](https://github.com/NativeScript/nativescript-cli/issues/686): Support building of XCode workspaces. +* [Implemented #687](https://github.com/NativeScript/nativescript-cli/issues/687): Android build with Gradle. +* [Implemented #729](https://github.com/NativeScript/nativescript-cli/issues/729): CocoaPods support from plugins. +* [Implemented #785](https://github.com/NativeScript/nativescript-cli/issues/785): Add platform on each platform related command. +* [Implemented #875](https://github.com/NativeScript/nativescript-cli/issues/875): Init command configure tns-core-modules version. + +### Fixed +* [Fixed #662](https://github.com/NativeScript/nativescript-cli/issues/662): Failed `tns platform add android` command leaves the project in inconsistent state. +* [Fixed #730](https://github.com/NativeScript/nativescript-cli/issues/730): `tns livesync android` throws stdout maxBuffer exceeded. +* [Fixed #772](https://github.com/NativeScript/nativescript-cli/issues/772): `tns platform update ios` command does not update metadata generator. +* [Fixed #793](https://github.com/NativeScript/nativescript-cli/issues/793): The NativeScript CLI writes errors on stdout. +* [Fixed #797](https://github.com/NativeScript/nativescript-cli/issues/797): Plugin add does not merge plugins's Info.plist file. +* [Fixed #811](https://github.com/NativeScript/nativescript-cli/issues/811): `tns livesync --watch` reports an error when platform specific file is changed. +* [Fixed #826](https://github.com/NativeScript/nativescript-cli/issues/826): Failed `tns prepare ` command leaves the project in inconsistent state. +* [Fixed #829](https://github.com/NativeScript/nativescript-cli/issues/829): Fail to build the project when `nativescript-telerik-ui` plugin is added before the platform. +* [Fixed #866](https://github.com/NativeScript/nativescript-cli/issues/866): The NativeScript CLI is not able to detect java on Ubuntu. +* [Fixed #896](https://github.com/NativeScript/nativescript-cli/issues/896): `tns run ` after `tns livesync ` starts the last synced app on the device. + 1.2.4 (2015, August 24) == diff --git a/CocoaPods.md b/CocoaPods.md new file mode 100644 index 0000000000..6bc2089e0e --- /dev/null +++ b/CocoaPods.md @@ -0,0 +1,89 @@ +# Using CocoaPods + +When you develop for iOS, you can quickly add third-party libraries to your NativeScript projects via the [CocoaPods](https://cocoapods.org/) dependency manager. + +To work with such libraries, you need to wrap them as a custom NativeScript plugin and add them to your project. + + * [Install CocoaPods](#install-cocoapods) + * [Create CLI Project](#create-cli-project) + * [Wrap the Library as NativeScript Plugin](#wrap-the-library-as-nativescript-plugin) + * [Build the Project](#build-the-project) + +## Install CocoaPods +You need to install CocoaPods. If you haven't yet, you can do so by running: + +``` +$ sudo gem install cocoapods +``` +> **NOTE:** All operations and code in this article are verified against CocoaPods 0.38.2. + +To check your current version, run the following command. + +``` +$ pod --version +``` + +To update CocoaPods, just run the installation command again. + +``` +sudo gem install cocoapods +``` + +## Create CLI Project +To start, create a project and add the iOS platform. + +```bash +$ tns create MYCocoaPods +$ cd MYCocoaPods +$ tns platform add ios +``` + +## Wrap the Library as NativeScript Plugin + +For more information about working with NativeScript plugins, click [here](PLUGINS.md). + +``` +cd .. +mkdir my-plugin +cd my-plugin +``` + +Create a package.json file with the following content: + +``` +{ + "name": "myplugin", + "version": "0.0.1", + "nativescript": { + "platforms": { + "ios": "1.3.0" + } + } +} +``` + +Create a [Podfile](https://guides.cocoapods.org/syntax/podfile.html) which describes the dependency to the library that you want to use. Move it to the `platforms/ios` folder. + +``` +my-plugin/ +├── package.json +└── platforms/ + └── ios/ + └── Podfile +``` + +Next, install the plugin: + +``` +tns plugin add ../my-plugin +``` + +## Build the project + +``` +tns build ios +``` + +This modifies the `MYCocoaPods.xcodeproj` and creates a workspace with the same name. + +> **IMPORTANT:** You will no longer be able to run the `xcodeproj` alone. NativeScript CLI will build the newly created workspace and produce the correct .ipa. \ No newline at end of file diff --git a/README.md b/README.md index 884b846a7a..63c6e042aa 100644 --- a/README.md +++ b/README.md @@ -69,24 +69,26 @@ You can install and run the NativeScript CLI on Windows or OS X. > On Windows systems, you can develop, build, and deploy NativeScript projects that target Android. * Windows Vista or later -* [Node.js 0.10.26][Node.js 0.10.26] or a later stable official release +* [Node.js 0.10.35][Node.js 0.10.35] or a later stable 0.x release * (Optional) [Chocolatey][Chocolatey] * [JDK 8][JDK 8] or a later stable official release -* [Apache Ant 1.8][Apache Ant 1.8] or a later stable official release -* [Android SDK 19][Android SDK 19] or a later stable official release +* [Gradle 2.3][Gradle 2.3] or a later stable official release +* [Android SDK 21][Android SDK 21] or a later stable official release +* [Android SDK Build-tools 22.0.0][Android SDK Build-tools 22.0.0] or a later stable official release +* [Android Support Repository][Android Support Repository] * (Optional) [Genymotion][Genymotion] If you want to develop for Android, verify that you have added the following paths in the `PATH` system environment variable. ``` -Path to the bin directory in the Apache Ant installation folder +Path to the bin directory in the Gradle installation folder Path to tools directory in the Android SDK installation folder Path to platform-tools directory in the Android SDK installation folder ``` -For example: PATH=...;...;C:\tools\apache-ant-1.9.4\bin;C:\Users\MyUser\AppData\Local\Android\android-sdk\tools;C:\Users\MyUser\AppData\Local\Android\android-sdk\platform-tools; +For example: PATH=...;...;C:\tools\gradle\bin;C:\Users\MyUser\AppData\Local\Android\android-sdk\tools;C:\Users\MyUser\AppData\Local\Android\android-sdk\platform-tools; -If you have installed Chocolatey, you can complete these steps to set up JDK, Apache Ant, and Android SDK. +If you have installed Chocolatey, you can complete these steps to set up JDK, Gradle, and Android SDK. 1. Run a Windows command prompt. 1. To install JDK, run the following command. @@ -94,27 +96,33 @@ If you have installed Chocolatey, you can complete these steps to set up JDK, Ap ```Shell choco install java.jdk ``` -1. If not present, create the following environment variable. +1. If not present, create the following environment variables. ``` JAVA_HOME=Path to the jdk* install folder ``` For example: JAVA_HOME=C:\Program Files\Java\jdk1.8.0_11 -1. To install Apache Ant, run the following command. + + ``` + ANDROID_HOME=Path to Android installation directory + ``` + + For example: ANDROID_HOME=C:\Android\android-sdk +1. To install Gradle, run the following command. ```Shell - choco install ant + choco install gradle ``` 1. If not present, add the following file path to the `PATH` system environment variable. ``` - Path to the bin directory in the Apache Ant installation folder + Path to the bin directory in the Gradle installation folder ``` - For example: PATH=...;...;C:\tools\apache-ant-1.9.4\bin + For example: PATH=...;...;C:\tools\gradle\bin 1. To install the Android SDK, run the following command. - + ```Shell choco install android-sdk ``` @@ -126,39 +134,68 @@ If you have installed Chocolatey, you can complete these steps to set up JDK, Ap ``` For example: PATH=...;...;C:\Users\MyUser\AppData\Local\Android\android-sdk\tools;C:\Users\MyUser\AppData\Local\Android\android-sdk\platform-tools -1. To update the Android SDK to 19 or later, run the following command. +1. To update the Android SDK to 21 or later, run the following command. ```Shell android update sdk ``` -1. Select all packages for the Android 19 SDK and any other SDKs that you want to install, click **Install** and wait for the installation to complete. +1. Select all packages for the Android 21 SDK and any other SDKs that you want to install, click **Install** and wait for the installation to complete. +1. Select Android SDK Build-tools 22.0.0 or later stable version, click **Install** and wait for the installation to complete. +1. Select Extras/Android Support Repository, click **Install** and wait for the installation to complete. + +> NOTE: You can install required Android Tools with the following command: +```Shell +android update sdk --filter tools,platform-tools,android-22,android-17,build-tools-22.0.1,sys-img-x86-android-22,extra-android-m2repository,extra-google-m2repository,extra-android-support --all --no-ui +``` ## OS X > On OS X systems, you can develop, build, and deploy NativeScript projects that target iOS and Android. * OS X Mavericks -* [Node.js 0.10.26][Node.js 0.10.26] or a later stable official release +* [Node.js 0.10.35][Node.js 0.10.35] or a later stable 0.x release * For iOS development * [Latest Xcode][12] * [Xcode command-line tools][12] + * (Optional) [CocoaPods 0.38.2][CocoaPods 0.38.2] * For Android development - * [JDK 8][JDK 8] or a later stable official release - * [Apache Ant 1.8][Apache Ant 1.8] or a later stable official release - * [Android SDK 19][Android SDK 19] or a later stable official release + * [JDK 8][JDK 8] or a later stable official release + * [Gradle 2.3][Gradle 2.3] or a later stable official release + * [Android SDK 21][Android SDK 21] or a later stable official release + * [Android SDK Build-tools 22.0.0][Android SDK Build-tools 22.0.0] or a later stable official release + * [Android Support Repository][Android Support Repository] * (Optional) [Genymotion][Genymotion] If you want to develop for Android, verify that you have added the following paths in your `PATH` in `~/.bash_profile`. ``` -Path to the bin subdirectory in the Apache Ant installation directory +Path to the bin subdirectory in the Gradle installation directory Path to the tools subdirectory in the Android SDK installation directory Path to the platform-tools subdirectory in the Android SDK installation directory ``` For example: ``` -export PATH=${PATH}:/ant/apache-ant-1.9.4/bin:/Applications/Android\ Studio.app/sdk/tools:/Applications/Android\ Studio.app/sdk/platform-tools +export PATH=${PATH}:/gradle/bin:/Applications/Android\ Studio.app/sdk/tools:/Applications/Android\ Studio.app/sdk/platform-tools +``` + +If not present, create the following environment variables. + +``` +JAVA_HOME=Path to the jdk* install folder +``` + +For example: JAVA_HOME=/usr/bin/java + +``` +ANDROID_HOME=Path to Android installation directory +``` + +For example: ANDROID_HOME=/Applications/Android\ Studio.app/sdk/ + +> NOTE: You can install required Android Tools with the following command: +```Shell +echo yes | android update sdk --filter tools,platform-tools,android-22,android-17,build-tools-22.0.1,sys-img-x86-android-22,extra-android-m2repository,extra-google-m2repository,extra-android-support --all --no-ui ``` ## Linux @@ -166,10 +203,10 @@ export PATH=${PATH}:/ant/apache-ant-1.9.4/bin:/Applications/Android\ Studio.app/ > On Linux systems, you can develop, build, and deploy NativeScript projects that target Android. * Ubuntu 14.04 LTS -* [Node.js 0.10.26][Node.js 0.10.26] or a later stable official release +* [Node.js 0.10.35][Node.js 0.10.35] or a later stable 0.x release > **TIP:** You can follow the instructions provided [here](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager) to install Node.js on your system. - + * G++ compiler ```Shell @@ -182,21 +219,42 @@ export PATH=${PATH}:/ant/apache-ant-1.9.4/bin:/Applications/Android\ Studio.app/ sudo apt-get install lib32z1 lib32ncurses5 lib32bz2-1.0 libstdc++6:i386 ``` * [JDK 8][JDK 8] or a later stable official release -* [Apache Ant 1.8][Apache Ant 1.8] or a later stable official release -* [Android SDK 19][Android SDK 19] or a later stable official release +* [Gradle 2.3][Gradle 2.3] or a later stable official release +* [Android SDK 21][Android SDK 21] or a later stable official release +* [Android SDK Build-tools 22.0.0][Android SDK Build-tools 22.0.0] or a later stable official release +* [Android Support Repository][Android Support Repository] * (Optional) [Genymotion][Genymotion] Verify that you have added the following paths in your `PATH`. ``` -Path to the bin subdirectory in the Apache Ant installation directory +Path to the bin subdirectory in the Gradle installation directory Path to the tools subdirectory in the Android SDK installation directory Path to the platform-tools subdirectory in the Android SDK installation directory ``` For example: ``` -export PATH=${PATH}:/ant/apache-ant-1.9.4/bin:/Applications/Android Studio.app/sdk/tools:/Applications/Android Studio.app/sdk/platform-tools +export PATH=${PATH}:/gradle/bin:/home/user/android-sdk/tools:/home/user/android-sdk/platform-tools +``` + +If not present, create the following environment variables. + +``` +JAVA_HOME=Path to the jdk* install folder +``` + +For example: JAVA_HOME=/usr/bin/java + +``` +ANDROID_HOME=Path to Android installation directory +``` + +For example: ANDROID_HOME=/home/user/android-sdk + +> NOTE: You can install required Android Tools with the following command: +```Shell +echo yes | android update sdk --filter tools,platform-tools,android-22,android-17,build-tools-22.0.1,sys-img-x86-android-22,extra-android-m2repository,extra-google-m2repository,extra-android-support --all --no-ui ``` [Back to Top][1] @@ -263,7 +321,7 @@ Run `tns help` to view all available commands in the browser. Run `tns help ` adds all libraries in the stated directory (Library Dir). * `prepare ` copies cross-platform and selected platform-specific content to the subdirectory for the target platform. * `build ` builds the project for the selected target platform. -* `emulate ` builds the project for the selected target platform and runs it in the native emulator, if configured. +* `emulate ` builds the project for the selected target platform and runs it in the native emulator, if configured. * `deploy [--device ]` deploys an already built application on connected device. * `run [--device ]` executes `prepare`, `build`, and `deploy`. * `livesync ` synchronizes changes from your project to an already deployed application on device. @@ -314,8 +372,8 @@ MyApp/ └── ... ``` -* The `app` directory is the **development space for your application**. You should modify all common and platform-specific code within this directory. When you run `prepare `, the NativeScript CLI copies relevant content to the platform-specific folders for each target platform. -* The `platforms` directory is created empty. When you add a target platform to your project, the NativeScript CLI creates a new subdirectory with the platform name. The subdirectory contains the ready-to-build resources of your app. When you run `prepare `, the NativeScript CLI copies relevant content from the `app` directory to the platform-specific subdirectory for each target platform.
In the `platforms` directory, you can safely modify configuration files such as `AndroidManifest.xml` and `Info.plist`. +* The `app` directory is the **development space for your application**. You should modify all common and platform-specific code within this directory. When you run `prepare `, the NativeScript CLI copies relevant content to the platform-specific folders for each target platform. +* The `platforms` directory is created empty. When you add a target platform to your project, the NativeScript CLI creates a new subdirectory with the platform name. The subdirectory contains the ready-to-build resources of your app. When you run `prepare `, the NativeScript CLI copies relevant content from the `app` directory to the platform-specific subdirectory for each target platform.
In the `platforms` directory, you can safely modify configuration files such as `AndroidManifest.xml` and `Info.plist`. [Back to Top][1] @@ -330,7 +388,7 @@ tns platform add android tns platform add ios ``` -`platform add` creates the `android` and the `ios` subdirectories in the `platforms` directory. These subdirectories have the platform-specific project structure required for native development with the native SDKs for the platform. +`platform add` creates the `android` and the `ios` subdirectories in the `platforms` directory. These subdirectories have the platform-specific project structure required for native development with the native SDKs for the platform. ``` ... @@ -380,9 +438,9 @@ For more information about working with NativeScript, see the following resource ### Development in `app` -The `app` directory in the root of the project is the development space for your project. **Place all your common and platform-specific code in this directory.** When you run `prepare `, the NativeScript CLI copies relevant content to the platform-specific folders for each target platform. +The `app` directory in the root of the project is the development space for your project. **Place all your common and platform-specific code in this directory.** When you run `prepare `, the NativeScript CLI copies relevant content to the platform-specific folders for each target platform. -In the `app` directory, you can use **platform-specific files** to provide customized functionality and design for each target platform. To indicate that a file is platform-specific, make sure that the file name is in the following format: `name.ios.extension` or `name.android.extension`. For example: `main.ios.js` or `main.android.js`. +In the `app` directory, you can use **platform-specific files** to provide customized functionality and design for each target platform. To indicate that a file is platform-specific, make sure that the file name is in the following format: `name.ios.extension` or `name.android.extension`. For example: `main.ios.js` or `main.android.js`. You can develop shared functionality or design in common files. To indicate that a file is common, make sure that the file name does not contain a `.android.` or `.ios.` string. @@ -420,7 +478,7 @@ tns build android tns build ios ``` -The NativeScript CLI calls the SDK for the selected target platform and uses it to build your app locally. +The NativeScript CLI calls the SDK for the selected target platform and uses it to build your app locally. When you build for Android, the NativeScript CLI saves the application package as an `APK` in `platforms` → `android` → `bin`. @@ -450,7 +508,7 @@ tns deploy android tns deploy ios ``` -The NativeScript CLI calls the SDK for the selected target platform and uses it to build your app locally. After the build is complete, the NativeScript CLI downloads and installs the application package on your connected devices. +The NativeScript CLI calls the SDK for the selected target platform and uses it to build your app locally. After the build is complete, the NativeScript CLI downloads and installs the application package on your connected devices. On Android devices, the app runs automatically. @@ -473,7 +531,7 @@ This operation calls the SDK for the selected target platform, builds your app l For Android, the NativeScript CLI runs your app in the earliest created virtual device or the currently running Android Virtual Device. Before running your app in the Android native emulator, make sure that you have configured at least one virtual device in the Android Virtual Device manager. -For iOS, the NativeScript CLI runs your app in the iOS Simulator. +For iOS, the NativeScript CLI runs your app in the iOS Simulator. [Back to Top][1] @@ -553,9 +611,9 @@ This software is licensed under the Apache 2.0 license, quoted ` Adds a locally stored native library to the current project. <% if(isHtml) { %>Copies the library files to the `lib/` folder in your project and configures the platform-specific projects in `platforms/` to work with the library. Build operations might perform additional configuration changes on the platform-specific project in `platforms/`.<% } %> +IMPORTANT: The `tns library add` command is deprecated and will be removed in a future release. Use the `tns plugin` set of commands instead. For more information, <% if(isHtml) { %>see the [plugin](plugin.html) set of commands.<% } %><% if(isConsole) { %>run `tns help plugin`.<% } %>. + IMPORTANT: When adding frameworks, keep in mind the following behaviors. * Any functionality exposed by the library will become available in the built application package. * The first build operation after you run this command is significantly slower. -<% if(isMacOS) { %>* When you add an iOS framework, the NativeScript CLI automatically changes your build target to iOS 8.0.<% } %> +<% if(isMacOS) { %>* When you add an iOS framework, the NativeScript CLI automatically changes your build target to iOS 8.0.<% } %> ### Attributes * `` is the target mobile platform for which you want to add a native library. You can set the following target platforms. * `android` - Adds an Android native library. <% if(isMacOS) { %>* `ios` - Adds an iOS native library.<% } %> -* `` is the file path to a locally stored framework.<% if(isHtml) { %> - When you want to add an iOS framework, `` must be the complete file path to the `*.framework` file that you want to use. - When you want to add an Android framework, `` might be any of the following: - +* `` is the file path to a locally stored framework.<% if(isHtml) { %> + When you want to add an iOS framework, `` must be the complete file path to the `*.framework` file that you want to use. + When you want to add an Android framework, `` might be any of the following: + * The file path to a directory containing one or more `*.jar` files. * The file path to a directory containing the `project.properties` files for an Android library project created with Eclipse. diff --git a/docs/man_pages/lib-management/library.md b/docs/man_pages/lib-management/library.md index 3e7fffd82c..756c69883d 100644 --- a/docs/man_pages/lib-management/library.md +++ b/docs/man_pages/lib-management/library.md @@ -7,11 +7,13 @@ General | `$ tns library ` Lets you manage third-party native libraries in your project. You must run the `library` command with a command extension. +IMPORTANT: The `tns library` command is deprecated and will be removed in a future release. Use the `tns plugin` set of commands instead. For more information, <% if(isHtml) { %>see the [plugin](plugin.html) set of commands.<% } %><% if(isConsole) { %>run `tns help plugin`.<% } %>. + ### Attributes `` extends the `library` command. You can set the following values for this attribute. * `add` - Adds a locally stored native library to the current project. -<% if(isHtml) { %> +<% if(isHtml) { %> ### Related Commands Command | Description diff --git a/lib/android-tools-info.ts b/lib/android-tools-info.ts index 4417591c45..a22d7ff4a9 100644 --- a/lib/android-tools-info.ts +++ b/lib/android-tools-info.ts @@ -30,7 +30,7 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { infoData.compileSdkVersion = this.getCompileSdk().wait(); infoData.buildToolsVersion = this.getBuildToolsVersion().wait(); infoData.targetSdkVersion = this.getTargetSdk().wait(); - infoData.supportLibraryVersion = this.getAndroidSupportLibVersion().wait(); + infoData.supportRepositoryVersion = this.getAndroidSupportRepositoryVersion().wait(); this.toolsInfo = infoData; } @@ -39,8 +39,9 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { }).future()(); } - public validateInfo(options?: {showWarningsAsErrors: boolean, validateTargetSdk: boolean}): IFuture { - return (() => { + public validateInfo(options?: {showWarningsAsErrors: boolean, validateTargetSdk: boolean}): IFuture { + return (() : boolean => { + let detectedErrors = false; this.showWarningsAsErrors = options && options.showWarningsAsErrors; let toolsInfoData = this.getToolsInfo().wait(); if(!toolsInfoData.androidHomeEnvVar || !this.$fs.exists(toolsInfoData.androidHomeEnvVar).wait()) { @@ -51,16 +52,28 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { if(!toolsInfoData.compileSdkVersion) { this.printMessage(`Cannot find a compatible Android SDK for compilation. To be able to build for Android, install Android SDK ${AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET} or later.`, "Run `$ android` to manage your Android SDK versions."); + detectedErrors = true; } if(!toolsInfoData.buildToolsVersion) { - this.printMessage(`You need to have the Android SDK Build-tools installed on your system. You can install any version in the following range: '${this.getBuildToolsRange()}'.`, + let buildToolsRange = this.getBuildToolsRange(); + let versionRangeMatches = buildToolsRange.match(/^.*?([\d\.]+)\s+.*?([\d\.]+)$/); + let message = `You can install any version in the following range: '${buildToolsRange}'.`; + + // Improve message in case buildToolsRange is something like: ">=22.0.0 <=22.0.0" - same numbers on both sides + if(versionRangeMatches && versionRangeMatches[1] && versionRangeMatches[2] && versionRangeMatches[1] === versionRangeMatches[2]) { + message = `You have to install version ${versionRangeMatches[1]}.`; + } + + this.printMessage("You need to have the Android SDK Build-tools installed on your system. " + message, 'Run "android" from your command-line to install required Android Build Tools.'); + detectedErrors = true; } - if(!toolsInfoData.supportLibraryVersion) { - this.printMessage(`You need to have the Android Support Library installed on your system. You can install any version in the following range: ${this.getAppCompatRange().wait() || ">=" + AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET}}.`, - 'Run `$ android` to manage the Android Support Library.'); + if(!toolsInfoData.supportRepositoryVersion) { + this.printMessage(`You need to have the latest Android Support Repository installed on your system.`, + 'Run `$ android` to manage the Android Support Repository.'); + detectedErrors = true; } if(options && options.validateTargetSdk) { @@ -71,13 +84,16 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { let minSupportedVersion = this.parseAndroidSdkString(_.first(supportedVersions)); if(targetSdk && (targetSdk < minSupportedVersion)) { - this.printMessage(`The selected Android target SDK ${newTarget} is not supported. You пкяш target ${minSupportedVersion} or later.`); + this.printMessage(`The selected Android target SDK ${newTarget} is not supported. You must target ${minSupportedVersion} or later.`); + detectedErrors = true; } else if(!targetSdk || targetSdk > this.getMaxSupportedVersion()) { this.$logger.warn(`Support for the selected Android target SDK ${newTarget} is not verified. Your Android app might not work as expected.`); } } } - }).future()(); + + return detectedErrors; + }).future()(); } /** @@ -146,10 +162,14 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { private getBuildToolsVersion(): IFuture { return ((): string => { - let pathToBuildTools = path.join(this.androidHome, "build-tools"); - let buildToolsRange = this.getBuildToolsRange(); + let buildToolsVersion: string; + if(this.androidHome) { + let pathToBuildTools = path.join(this.androidHome, "build-tools"); + let buildToolsRange = this.getBuildToolsRange(); + buildToolsVersion = this.getMatchingDir(pathToBuildTools, buildToolsRange).wait(); + } - return this.getMatchingDir(pathToBuildTools, buildToolsRange).wait(); + return buildToolsVersion; }).future()(); } @@ -165,11 +185,15 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { }).future()(); } - private getAndroidSupportLibVersion(): IFuture { + private getAndroidSupportRepositoryVersion(): IFuture { return ((): string => { - let pathToAppCompat = path.join(this.androidHome, "extras", "android", "m2repository", "com", "android", "support", "appcompat-v7"); + let selectedAppCompatVersion: string; let requiredAppCompatRange = this.getAppCompatRange().wait(); - let selectedAppCompatVersion = requiredAppCompatRange ? this.getMatchingDir(pathToAppCompat, requiredAppCompatRange).wait() : undefined; + if(this.androidHome && requiredAppCompatRange) { + let pathToAppCompat = path.join(this.androidHome, "extras", "android", "m2repository", "com", "android", "support", "appcompat-v7"); + selectedAppCompatVersion = this.getMatchingDir(pathToAppCompat, requiredAppCompatRange).wait(); + } + this.$logger.trace(`Selected AppCompat version is: ${selectedAppCompatVersion}`); return selectedAppCompatVersion; }).future()(); diff --git a/lib/commands/add-library.ts b/lib/commands/add-library.ts index 002e16e0a6..dbbba9021f 100644 --- a/lib/commands/add-library.ts +++ b/lib/commands/add-library.ts @@ -13,6 +13,7 @@ export class AddLibraryCommand implements ICommand { execute(args: string[]): IFuture { return (() => { + this.$logger.warn("IMPORTANT: The `tns library add` command is deprecated and will be removed in a future release. Use the plugin set of commands instead. For more information, run `tns help plugin`."); let platform = args[0]; let libraryPath = path.resolve(args[1]); this.$platformService.addLibrary(platform, libraryPath).wait(); diff --git a/lib/common b/lib/common index 93a73565fa..a13a42f84e 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 93a73565fae071838c6cb2a1bc2af6523ac6a868 +Subproject commit a13a42f84e959df8315b821c9102fabbdb0424e9 diff --git a/lib/declarations.ts b/lib/declarations.ts index e1f7b147ae..756208bc43 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -105,9 +105,9 @@ interface IAndroidToolsInfo { /** * Validates the information about required Android tools and SDK versions. * @param {any} options Defines if the warning messages should treated as error and if the targetSdk value should be validated as well. - * @return {void} + * @return {boolean} True if there are detected issues, false otherwise. */ - validateInfo(options?: {showWarningsAsErrors: boolean, validateTargetSdk: boolean}): IFuture; + validateInfo(options?: {showWarningsAsErrors: boolean, validateTargetSdk: boolean}): IFuture; } /** @@ -130,9 +130,9 @@ interface IAndroidToolsInfoData { compileSdkVersion: number; /** - * The latest installed version of Android Support Library that satisfies CLI's requirements. + * The latest installed version of Android Support Repository that satisfies CLI's requirements. */ - supportLibraryVersion: string; + supportRepositoryVersion: string; /** * The Android targetSdkVersion specified by the user. diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index d8022318da..29108c0432 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -33,7 +33,7 @@ interface IBuildConfig { interface IPlatformProjectService { platformData: IPlatformData; validate(): IFuture; - createProject(projectRoot: string, frameworkDir: string): IFuture; + createProject(frameworkDir: string, frameworkVersion: string): IFuture; interpolateData(projectRoot: string): IFuture; afterCreateProject(projectRoot: string): IFuture; buildProject(projectRoot: string, buildConfig?: IBuildConfig): IFuture; @@ -42,7 +42,11 @@ interface IPlatformProjectService { isPlatformPrepared(projectRoot: string): IFuture; addLibrary(libraryPath: string): IFuture; canUpdatePlatform(currentVersion: string, newVersion: string): IFuture; - updatePlatform(currentVersion: string, newVersion: string): IFuture; + /** + * Provides a platform specific update logic for the specified runtime versions. + * @return true in cases when the update procedure should continue. + */ + updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean, addPlatform?: Function, removePlatform?: (platforms: string[]) => IFuture): IFuture; preparePluginNativeCode(pluginData: IPluginData, options?: any): IFuture; removePluginNativeCode(pluginData: IPluginData): IFuture; afterPrepareAllPlugins(): IFuture; diff --git a/lib/services/android-debug-service.ts b/lib/services/android-debug-service.ts index c4184ef89c..a35f90c112 100644 --- a/lib/services/android-debug-service.ts +++ b/lib/services/android-debug-service.ts @@ -95,7 +95,7 @@ class AndroidDebugService implements IDebugService { private printDebugPort(packageName: string): IFuture { return (() => { - let res = this.device.adb.executeShellCommand(["am", "broadcast", "-a", packageName + "-GetDgbPort"]).wait(); + let res = this.device.adb.executeShellCommand(["am", "broadcast", "-a", packageName + "-GetDbgPort"]).wait(); this.$logger.info(res); }).future()(); } diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 66dac5a48a..3625002a76 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -21,14 +21,14 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService private $androidToolsInfo: IAndroidToolsInfo, private $childProcess: IChildProcess, private $errors: IErrors, + $fs: IFileSystem, private $hostInfo: IHostInfo, private $logger: ILogger, private $options: IOptions, private $projectData: IProjectData, private $projectDataService: IProjectDataService, private $propertiesParser: IPropertiesParser, - private $sysInfo: ISysInfo, - $fs: IFileSystem) { + private $sysInfo: ISysInfo) { super($fs); this._androidProjectPropertiesManagers = Object.create(null); } @@ -60,9 +60,13 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService return this._platformData; } - public getAppResourcesDestinationDirectoryPath(): IFuture { + public getAppResourcesDestinationDirectoryPath(frameworkVersion?: string): IFuture { return (() => { - return path.join(this.platformData.projectRoot, "src", "main", "res"); + if(this.canUseGradle(frameworkVersion).wait()) { + return path.join(this.platformData.projectRoot, "src", "main", "res"); + } + + return path.join(this.platformData.projectRoot, "res"); }).future()(); } @@ -76,9 +80,8 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService }).future()(); } - public createProject(projectRoot: string, frameworkDir: string): IFuture { + public createProject(frameworkDir: string, frameworkVersion: string): IFuture { return (() => { - let frameworkVersion = this.$fs.readJson(path.join(frameworkDir, "../", "package.json")).wait().version; if(semver.lt(frameworkVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) { this.$errors.failWithoutHelp(`The NativeScript CLI requires Android runtime ${AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE} or later to work properly.`); } @@ -86,30 +89,30 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService // TODO: Move these check to validate method once we do not support ant. this.checkGradle().wait(); - this.$fs.ensureDirectoryExists(projectRoot).wait(); + this.$fs.ensureDirectoryExists(this.platformData.projectRoot).wait(); let androidToolsInfo = this.$androidToolsInfo.getToolsInfo().wait(); - let newTarget = androidToolsInfo.targetSdkVersion; - this.$logger.trace(`Using Android SDK '${newTarget}'.`); + let targetSdkVersion = androidToolsInfo.targetSdkVersion; + this.$logger.trace(`Using Android SDK '${targetSdkVersion}'.`); if(this.$options.symlink) { - this.symlinkDirectory("build-tools", projectRoot, frameworkDir).wait(); - this.symlinkDirectory("libs", projectRoot, frameworkDir).wait(); - this.symlinkDirectory("src", projectRoot, frameworkDir).wait(); + this.symlinkDirectory("build-tools", this.platformData.projectRoot, frameworkDir).wait(); + this.symlinkDirectory("libs", this.platformData.projectRoot, frameworkDir).wait(); + this.symlinkDirectory("src", this.platformData.projectRoot, frameworkDir).wait(); - this.$fs.symlink(path.join(frameworkDir, "build.gradle"), path.join(projectRoot, "build.gradle")).wait(); - this.$fs.symlink(path.join(frameworkDir, "settings.gradle"), path.join(projectRoot, "settings.gradle")).wait(); + this.$fs.symlink(path.join(frameworkDir, "build.gradle"), path.join(this.platformData.projectRoot, "build.gradle")).wait(); + this.$fs.symlink(path.join(frameworkDir, "settings.gradle"), path.join(this.platformData.projectRoot, "settings.gradle")).wait(); } else { - this.copy(projectRoot, frameworkDir, "build-tools libs src", "-R"); - this.copy(projectRoot, frameworkDir, "build.gradle settings.gradle", "-f"); + this.copy(this.platformData.projectRoot, frameworkDir, "build-tools libs src", "-R"); + this.copy(this.platformData.projectRoot, frameworkDir, "build.gradle settings.gradle", "-f"); } - this.cleanResValues(newTarget).wait(); + this.cleanResValues(targetSdkVersion, frameworkVersion).wait(); }).future()(); } - private cleanResValues(versionNumber: number): IFuture { + private cleanResValues(targetSdkVersion: number, frameworkVersion: string): IFuture { return (() => { - let resDestinationDir = this.getAppResourcesDestinationDirectoryPath().wait(); + let resDestinationDir = this.getAppResourcesDestinationDirectoryPath(frameworkVersion).wait(); let directoriesInResFolder = this.$fs.readDirectory(resDestinationDir).wait(); let directoriesToClean = directoriesInResFolder .map(dir => { return { @@ -119,7 +122,7 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService }) .filter(dir => dir.dirName.match(AndroidProjectService.VALUES_VERSION_DIRNAME_PREFIX) && dir.sdkNum - && (!versionNumber || (versionNumber < dir.sdkNum))) + && (!targetSdkVersion || (targetSdkVersion < dir.sdkNum))) .map(dir => path.join(resDestinationDir, dir.dirName)); this.$logger.trace("Directories to clean:"); this.$logger.trace(directoriesToClean); @@ -148,11 +151,20 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService } public canUpdatePlatform(currentVersion: string, newVersion: string): IFuture { - return Future.fromResult(true); + return Future.fromResult(true); } - public updatePlatform(currentVersion: string, newVersion: string): IFuture { - return Future.fromResult(); + public updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean, addPlatform?: Function, removePlatforms?: (platforms: string[]) => IFuture): IFuture { + return (() => { + if(semver.eq(newVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) { + let platformLowercase = this.platformData.normalizedPlatformName.toLowerCase(); + removePlatforms([platformLowercase.split("@")[0]]).wait(); + addPlatform(platformLowercase).wait(); + return false; + } + + return true; + }).future()(); } public buildProject(projectRoot: string, buildConfig?: IBuildConfig): IFuture { @@ -163,7 +175,7 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService let compileSdk = androidToolsInfo.compileSdkVersion; let targetSdk = this.getTargetFromAndroidManifest().wait() || compileSdk; let buildToolsVersion = androidToolsInfo.buildToolsVersion; - let appCompatVersion = androidToolsInfo.supportLibraryVersion; + let appCompatVersion = androidToolsInfo.supportRepositoryVersion; let buildOptions = ["buildapk", `-PcompileSdk=android-${compileSdk}`, `-PtargetSdk=${targetSdk}`, @@ -189,6 +201,8 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService let args = this.getAntArgs(this.$options.release ? "release" : "debug", projectRoot); this.spawn('ant', args).wait(); + + this.platformData.deviceBuildOutputPath = path.join(this.platformData.projectRoot, "bin"); } }).future()(); } @@ -271,11 +285,19 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService return Future.fromResult(); } - private canUseGradle(): IFuture { + private _canUseGradle: boolean; + private canUseGradle(frameworkVersion?: string): IFuture { return (() => { - this.$projectDataService.initialize(this.$projectData.projectDir); - let frameworkVersion = this.$projectDataService.getValue(this.platformData.frameworkPackageName).wait().version; - return semver.gte(frameworkVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE); + if(!this._canUseGradle) { + if(!frameworkVersion) { + this.$projectDataService.initialize(this.$projectData.projectDir); + frameworkVersion = this.$projectDataService.getValue(this.platformData.frameworkPackageName).wait().version; + } + + this._canUseGradle = semver.gte(frameworkVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE); + } + + return this._canUseGradle; }).future()(); } @@ -402,7 +424,7 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService try { this.$childProcess.exec('android list targets').wait(); } catch(error) { - if (error.match(/command\snot\sfound/)) { + if (error.message.match(/command\snot\sfound/)) { this.$errors.fail("The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path."); } else { this.$errors.fail("An error occurred while listing Android targets"); diff --git a/lib/services/doctor-service.ts b/lib/services/doctor-service.ts index dd12b92ca2..1f8aae0c70 100644 --- a/lib/services/doctor-service.ts +++ b/lib/services/doctor-service.ts @@ -69,12 +69,14 @@ class DoctorService implements IDoctorService { this.$logger.warn("WARNING: Gradle is not installed or is not configured properly."); this.$logger.out("You will not be able to build your projects for Android or run them in the emulator or on a connected device." + EOL + "To be able to build for Android and run apps in the emulator or on a connected device, verify that you have installed Gradle."); + result = true; } if(sysInfo.gradleVer && helpers.versionCompare(sysInfo.gradleVer, DoctorService.MIN_SUPPORTED_GRADLE_VERSION) === -1) { this.$logger.warn(`WARNING: Gradle version is lower than ${DoctorService.MIN_SUPPORTED_GRADLE_VERSION}.`); this.$logger.out("You will not be able to build your projects for Android or run them in the emulator or on a connected device." + EOL + `To be able to build for Android and run apps in the emulator or on a connected device, verify thqt you have at least ${DoctorService.MIN_SUPPORTED_GRADLE_VERSION} version installed.`); + result = true; } if(!sysInfo.javacVersion) { @@ -82,10 +84,11 @@ class DoctorService implements IDoctorService { this.$logger.out("You will not be able to build your projects for Android." + EOL + "To be able to build for Android, verify that you have installed The Java Development Kit (JDK) and configured it according to system requirements as" + EOL + " described in https://github.com/NativeScript/nativescript-cli#system-requirements."); + result = true; } - this.$androidToolsInfo.validateInfo().wait(); - return result; + let androidToolsIssues = this.$androidToolsInfo.validateInfo().wait(); + return result || androidToolsIssues; } private printPackageManagerTip() { diff --git a/lib/services/init-service.ts b/lib/services/init-service.ts index 865c6d3dbe..c02dd2b6db 100644 --- a/lib/services/init-service.ts +++ b/lib/services/init-service.ts @@ -9,7 +9,8 @@ import semver = require("semver"); export class InitService implements IInitService { private static MIN_SUPPORTED_FRAMEWORK_VERSIONS: IStringDictionary = { "tns-ios": "1.1.0", - "tns-android": "1.1.0" + "tns-android": "1.1.0", + "tns-core-modules": "1.2.0" }; private _projectFilePath: string; @@ -56,6 +57,12 @@ export class InitService implements IInitService { }); } + let dependencies = projectData.dependencies; + if(!dependencies) { + projectData.dependencies = Object.create(null); + } + projectData.dependencies[constants.TNS_CORE_MODULES_NAME] = this.getVersionData(constants.TNS_CORE_MODULES_NAME).wait()["version"]; + this.$fs.writeJson(this.projectFilePath, projectData).wait(); } catch(err) { this.$fs.writeJson(this.projectFilePath, projectDataBackup).wait(); diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index 2692206772..32d78c6685 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -202,16 +202,21 @@ class IOSDebugService implements IDebugService { } catch (e) { this.$errors.failWithoutHelp(`The application ${projectId} timed out when performing the NativeScript debugger handshake.`); } + this.readyForAttachAction(iosDevice).wait(); break; case readyForAttach: - createWebSocketProxy(this.$logger, (callback) => connectEventually(() => iosDevice.connectToPort(InspectorBackendPort), callback)); - this.executeOpenDebuggerClient().wait(); + this.readyForAttachAction(iosDevice).wait(); break; } }).future()()).wait(); }).future()(); } + private readyForAttachAction(iosDevice: iOSDevice.IOSDevice): IFuture { + createWebSocketProxy(this.$logger, (callback) => connectEventually(() => iosDevice.connectToPort(InspectorBackendPort), callback)); + return this.executeOpenDebuggerClient(); + } + public executeOpenDebuggerClient(): IFuture { if (this.$options.client) { return this.openDebuggingClient(); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 3e05bf6a7c..55e8056d97 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -11,7 +11,7 @@ import * as constants from "../constants"; import * as helpers from "../common/helpers"; import * as projectServiceBaseLib from "./platform-project-service-base"; -export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService { +export 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__"; @@ -30,7 +30,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $iOSEmulatorServices: Mobile.IEmulatorPlatformServices, private $options: IOptions, private $injector: IInjector, - private $projectDataService: IProjectDataService) { + private $projectDataService: IProjectDataService, + private $prompter: IPrompter) { super($fs); } @@ -96,23 +97,23 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ }).future()(); } - public createProject(projectRoot: string, frameworkDir: string): IFuture { + public createProject(frameworkDir: string, frameworkVersion: string): IFuture { return (() => { - this.$fs.ensureDirectoryExists(path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)).wait(); + this.$fs.ensureDirectoryExists(path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)).wait(); if(this.$options.symlink) { let xcodeProjectName = util.format("%s.xcodeproj", IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER); - shell.cp("-R", path.join(frameworkDir, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER, "*"), path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)); - shell.cp("-R", path.join(frameworkDir, xcodeProjectName), projectRoot); + shell.cp("-R", path.join(frameworkDir, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER, "*"), path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)); + shell.cp("-R", path.join(frameworkDir, xcodeProjectName), this.platformData.projectRoot); let directoryContent = this.$fs.readDirectory(frameworkDir).wait(); let frameworkFiles = _.difference(directoryContent, [IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER, xcodeProjectName]); _.each(frameworkFiles, (file: string) => { - this.$fs.symlink(path.join(frameworkDir, file), path.join(projectRoot, file)).wait(); + this.$fs.symlink(path.join(frameworkDir, file), path.join(this.platformData.projectRoot, file)).wait(); }); } else { - shell.cp("-R", path.join(frameworkDir, "*"), projectRoot); + shell.cp("-R", path.join(frameworkDir, "*"), this.platformData.projectRoot); } }).future()(); } @@ -274,24 +275,33 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ }).future()(); } - public updatePlatform(currentVersion: string, newVersion: string): IFuture { + public updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean): IFuture { return (() => { - // Copy old file to options["profile-dir"] - let sourceFile = path.join(this.platformData.projectRoot, util.format("%s.xcodeproj", this.$projectData.projectName)); - let destinationFile = path.join(this.$options.profileDir, "xcodeproj"); - this.$fs.deleteDirectory(destinationFile).wait(); - shell.cp("-R", path.join(sourceFile, "*"), destinationFile); - this.$logger.info("Backup file %s at location %s", sourceFile, destinationFile); - this.$fs.deleteDirectory(path.join(this.platformData.projectRoot, util.format("%s.xcodeproj", this.$projectData.projectName))).wait(); - - // Copy xcodeProject file - let cachedPackagePath = path.join(this.$npmInstallationManager.getCachedPackagePath(this.platformData.frameworkPackageName, newVersion), constants.PROJECT_FRAMEWORK_FOLDER_NAME, util.format("%s.xcodeproj", IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)); - shell.cp("-R", path.join(cachedPackagePath, "*"), path.join(this.platformData.projectRoot, util.format("%s.xcodeproj", this.$projectData.projectName))); - this.$logger.info("Copied from %s at %s.", cachedPackagePath, this.platformData.projectRoot); - - let pbxprojFilePath = path.join(this.platformData.projectRoot, this.$projectData.projectName + IOSProjectService.XCODE_PROJECT_EXT_NAME, "project.pbxproj"); - this.replaceFileContent(pbxprojFilePath).wait(); - }).future()(); + if(!canUpdate) { + 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(); + if(isUpdateConfirmed) { + // Copy old file to options["profile-dir"] + let sourceDir = path.join(this.platformData.projectRoot, `${this.$projectData.projectName}.xcodeproj`); + let destinationDir = path.join(this.$options.profileDir, "xcodeproj"); + this.$fs.deleteDirectory(destinationDir).wait(); + shell.cp("-R", path.join(sourceDir, "*"), destinationDir); + this.$logger.info(`Backup file ${sourceDir} at location ${destinationDir}`); + this.$fs.deleteDirectory(sourceDir).wait(); + + // Copy xcodeProject file + let cachedPackagePath = path.join(this.$npmInstallationManager.getCachedPackagePath(this.platformData.frameworkPackageName, newVersion), constants.PROJECT_FRAMEWORK_FOLDER_NAME, util.format("%s.xcodeproj", IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)); + shell.cp("-R", path.join(cachedPackagePath, "*"), sourceDir); + this.$logger.info(`Copied from ${cachedPackagePath} at ${this.platformData.projectRoot}.`); + + let pbxprojFilePath = path.join(this.platformData.projectRoot, this.$projectData.projectName + IOSProjectService.XCODE_PROJECT_EXT_NAME, "project.pbxproj"); + this.replaceFileContent(pbxprojFilePath).wait(); + } + + return isUpdateConfirmed; + } + + return true; + }).future()(); } public prepareProject(): IFuture { diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index f366610e5b..258801bc53 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -93,8 +93,8 @@ export class PlatformService implements IPlatformService { private addPlatformCore(platformData: IPlatformData, frameworkDir: string): IFuture { return (() => { - platformData.platformProjectService.createProject(platformData.projectRoot, frameworkDir).wait(); let installedVersion = this.$fs.readJson(path.join(frameworkDir, "../", "package.json")).wait().version; + platformData.platformProjectService.createProject(frameworkDir, installedVersion).wait(); if(this.$options.frameworkPath && this.$fs.getFsStats(this.$options.frameworkPath).wait().isFile() && !this.$options.symlink) { // Need to remove unneeded node_modules folder @@ -425,74 +425,72 @@ export class PlatformService implements IPlatformService { this.ensurePackageIsCached(platformData.frameworkPackageName, newVersion).wait(); - if(platformData.platformProjectService.canUpdatePlatform(currentVersion, newVersion).wait()) { - + let canUpdate = platformData.platformProjectService.canUpdatePlatform(currentVersion, newVersion).wait(); + if(canUpdate) { if(!semver.valid(newVersion)) { this.$errors.fail("The version %s is not valid. The version should consists from 3 parts separated by dot.", newVersion); } if(semver.gt(currentVersion, newVersion)) { // Downgrade - let isUpdateConfirmed = this.$prompter.confirm(`You are going to downgrade to android runtime v.${newVersion}. Are you sure?`, () => false).wait(); + let isUpdateConfirmed = this.$prompter.confirm(`You are going to downgrade to runtime v.${newVersion}. Are you sure?`, () => false).wait(); if(isUpdateConfirmed) { - this.updatePlatformCore(platformData, currentVersion, newVersion).wait(); + this.updatePlatformCore(platformData, currentVersion, newVersion, canUpdate).wait(); } } else if(semver.eq(currentVersion, newVersion)) { this.$errors.fail("Current and new version are the same."); } else { - this.updatePlatformCore(platformData, currentVersion, newVersion).wait(); + this.updatePlatformCore(platformData, currentVersion, newVersion, canUpdate).wait(); } } else { - 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(); - if(isUpdateConfirmed) { - platformData.platformProjectService.updatePlatform(currentVersion, newVersion).wait(); - this.updatePlatformCore(platformData, currentVersion, newVersion).wait(); - } + this.updatePlatformCore(platformData, currentVersion, newVersion, canUpdate).wait(); } }).future()(); } - private updatePlatformCore(platformData: IPlatformData, currentVersion: string, newVersion: string): IFuture { + private updatePlatformCore(platformData: IPlatformData, currentVersion: string, newVersion: string, canUpdate: boolean): IFuture { return (() => { - // Remove old framework files - let oldFrameworkData = this.getFrameworkFiles(platformData, currentVersion).wait(); - - _.each(oldFrameworkData.frameworkFiles, file => { - let fileToDelete = path.join(platformData.projectRoot, file); - this.$logger.trace("Deleting %s", fileToDelete); - this.$fs.deleteFile(fileToDelete).wait(); - }); - - _.each(oldFrameworkData.frameworkDirectories, dir => { - let dirToDelete = path.join(platformData.projectRoot, dir); - this.$logger.trace("Deleting %s", dirToDelete); - this.$fs.deleteDirectory(dirToDelete).wait(); - }); + let update = platformData.platformProjectService.updatePlatform(currentVersion, newVersion, canUpdate, this.addPlatform.bind(this), this.removePlatforms.bind(this)).wait(); + if(update) { + // Remove old framework files + let oldFrameworkData = this.getFrameworkFiles(platformData, currentVersion).wait(); + + _.each(oldFrameworkData.frameworkFiles, file => { + let fileToDelete = path.join(platformData.projectRoot, file); + this.$logger.trace("Deleting %s", fileToDelete); + this.$fs.deleteFile(fileToDelete).wait(); + }); - // Add new framework files - let newFrameworkData = this.getFrameworkFiles(platformData, newVersion).wait(); - let cacheDirectoryPath = this.$npmInstallationManager.getCachedPackagePath(platformData.frameworkPackageName, newVersion); + _.each(oldFrameworkData.frameworkDirectories, dir => { + let dirToDelete = path.join(platformData.projectRoot, dir); + this.$logger.trace("Deleting %s", dirToDelete); + this.$fs.deleteDirectory(dirToDelete).wait(); + }); - _.each(newFrameworkData.frameworkFiles, file => { - let sourceFile = path.join(cacheDirectoryPath, constants.PROJECT_FRAMEWORK_FOLDER_NAME, file); - let destinationFile = path.join(platformData.projectRoot, file); - this.$logger.trace("Replacing %s with %s", sourceFile, destinationFile); - shell.cp("-f", sourceFile, destinationFile); - }); + // Add new framework files + let newFrameworkData = this.getFrameworkFiles(platformData, newVersion).wait(); + let cacheDirectoryPath = this.$npmInstallationManager.getCachedPackagePath(platformData.frameworkPackageName, newVersion); - _.each(newFrameworkData.frameworkDirectories, dir => { - let sourceDirectory = path.join(cacheDirectoryPath, constants.PROJECT_FRAMEWORK_FOLDER_NAME, dir); - let destinationDirectory = path.join(platformData.projectRoot, dir); - this.$logger.trace("Copying %s to %s", sourceDirectory, destinationDirectory); - shell.cp("-fR", path.join(sourceDirectory, "*"), destinationDirectory); - }); + _.each(newFrameworkData.frameworkFiles, file => { + let sourceFile = path.join(cacheDirectoryPath, constants.PROJECT_FRAMEWORK_FOLDER_NAME, file); + let destinationFile = path.join(platformData.projectRoot, file); + this.$logger.trace("Replacing %s with %s", sourceFile, destinationFile); + shell.cp("-f", sourceFile, destinationFile); + }); - // Update .tnsproject file - this.$projectDataService.initialize(this.$projectData.projectDir); - this.$projectDataService.setValue(platformData.frameworkPackageName, {version: newVersion}).wait(); + _.each(newFrameworkData.frameworkDirectories, dir => { + let sourceDirectory = path.join(cacheDirectoryPath, constants.PROJECT_FRAMEWORK_FOLDER_NAME, dir); + let destinationDirectory = path.join(platformData.projectRoot, dir); + this.$logger.trace("Copying %s to %s", sourceDirectory, destinationDirectory); + shell.cp("-fR", path.join(sourceDirectory, "*"), destinationDirectory); + }); - this.$logger.out("Successfully updated to version ", newVersion); + // Update .tnsproject file + this.$projectDataService.initialize(this.$projectData.projectDir); + this.$projectDataService.setValue(platformData.frameworkPackageName, {version: newVersion}).wait(); + this.$logger.out("Successfully updated to version ", newVersion); + } }).future()(); } diff --git a/lib/services/plugins-service.ts b/lib/services/plugins-service.ts index 7e0b8efd31..3258b76e02 100644 --- a/lib/services/plugins-service.ts +++ b/lib/services/plugins-service.ts @@ -117,7 +117,7 @@ export class PluginsService implements IPluginsService { this.$projectFilesManager.processPlatformSpecificFiles(pluginDestinationPath, platform).wait(); pluginData.pluginPlatformsFolderPath = (_platform: string) => path.join(pluginData.fullPath, "platforms", _platform); - platformData.platformProjectService.preparePluginNativeCode(pluginData, {executePodInstall: true }).wait(); + platformData.platformProjectService.preparePluginNativeCode(pluginData).wait(); shelljs.rm("-rf", path.join(pluginDestinationPath, pluginData.name, "platforms")); diff --git a/lib/services/usb-livesync-service.ts b/lib/services/usb-livesync-service.ts index 42814b1f17..e7eda28584 100644 --- a/lib/services/usb-livesync-service.ts +++ b/lib/services/usb-livesync-service.ts @@ -149,6 +149,9 @@ export class AndroidUsbLiveSyncService extends androidLiveSyncServiceLib.Android public restartApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture { return (() => { + this.device.adb.executeShellCommand(["chmod", "777", deviceAppData.deviceProjectRootPath]).wait(); + this.device.adb.executeShellCommand(["chmod", "777", `/data/local/tmp/${deviceAppData.appIdentifier}`]).wait(); + if(this.$options.companion) { let commands = [ this.liveSyncCommands.SyncFilesCommand() ]; this.livesync(deviceAppData.appIdentifier, deviceAppData.deviceProjectRootPath, commands).wait(); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 5ec70d6dcb..3908366680 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -38,6 +38,7 @@ function createTestInjector(projectPath: string, projectName: string): IInjector testInjector.register("projectHelper", {}); testInjector.register("staticConfig", ConfigLib.StaticConfig); testInjector.register("projectDataService", {}); + testInjector.register("prompter", {}); return testInjector; } @@ -215,7 +216,7 @@ describe("Static libraries support", () => { assert.equal(modulemap, modulemapExpectation); - // Delete all header files. And try to regenerate modulemap. + // Delete all header files. And try to regenerate modulemap. _.each(headers, header => { fs.deleteFile(path.join(staticLibraryHeadersPath, header)).wait(); }); iOSProjectService.generateMobulemap(staticLibraryHeadersPath, libraryName); diff --git a/test/stubs.ts b/test/stubs.ts index 5d41ce0cf3..70545e8233 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -23,8 +23,6 @@ export class LoggerStub implements ILogger { write(...args: string[]): void { } - printMarkdown(...args: string[]): void { } - prepare(item: any): string { return ""; } @@ -33,6 +31,8 @@ export class LoggerStub implements ILogger { printMsgWithTimeout(message: string, timeout: number): IFuture { return null; } + + printMarkdown(message: string): void { } } export class FileSystemStub implements IFileSystem { @@ -307,8 +307,8 @@ export class PlatformProjectServiceStub implements IPlatformProjectService { canUpdatePlatform(currentVersion: string, newVersion: string): IFuture { return Future.fromResult(false); } - updatePlatform(currentVersion: string, newVersion: string): IFuture { - return Future.fromResult(); + updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean): IFuture { + return Future.fromResult(true); } prepareAppResources(appResourcesDirectoryPath: string): IFuture { return Future.fromResult();