-
-
Notifications
You must be signed in to change notification settings - Fork 196
Plamen5kov/rebuild aar #3398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plamen5kov/rebuild aar #3398
Conversation
ae4f213
to
b96a8cc
Compare
); | ||
} | ||
|
||
return this.getDirectories(source).filter(isAndroidSourceDirectory); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using the following approach
const directories = ["res", "java", "assets", "jniLibs"];
return this.$fs.enumerateFilesInDirectorySync(source, (file, stat) => stat.isDirectory() && _.includes(file, directoties));
return this.getDirectories(source).filter(isAndroidSourceDirectory); | ||
} | ||
|
||
private getManifest(platformsDir: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use platformsData.getPlatformData().configurationFilePath
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the plugin's platforms dir, not the project
return this.$fs.readDirectory(source).map(name => path.join(source, name)); | ||
} | ||
|
||
private createDirIfDoesntExist(dirPath: string, { isRelativeToScript = false } = {}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use this.$fs.ensureDirectoryExists(dirPath)
?
@Plamen5kov can you please rebase the branch correctly, so it will be easier to check the new commits. Also currently it conflicts and cannot be merged. |
e177efe
to
10d60af
Compare
@rosen-vladimirov I did |
10d60af
to
d59dbe7
Compare
b96a8cc
to
2b5ef94
Compare
private getAndroidSourceDirectories(source: string): Array<string> { | ||
const directories = ["res", "java", "assets", "jniLibs"]; | ||
const resultArr: Array<string> = []; | ||
this.$fs.enumerateFilesInDirectorySync(source, (file, stat) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return this.$fs.enumerateFilesInDirectorySync(source, (file, stat) => stat.isDirectory() && _.some(directories, (element) => file.endsWith(element)))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't work, returns true or false i need the array
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gradle-plugin is an external tool, so it should be placed in a separate location, not in services. A common place for such tools is a vendor directory at the root of the project. You can place the gradle-plugin there. Also note that using such tools directly in the package requires adding their LICENSE file as well, so can you please add it as well.
import * as path from "path"; | ||
import * as shell from "shelljs"; | ||
|
||
const xml2js = require("xml2js"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add @types/xml2js
as devDependency and use import here;
import { Builder, parseString } from "xml2js";
return resultArr; | ||
} | ||
|
||
private getManifest(platformsDir: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method needs return type
return newManifestContent; | ||
} | ||
|
||
private createManifestContent(packageName: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method needs return type
return newManifestContent; | ||
} | ||
|
||
private async getXml(stringContent: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method needs return type
if (manifestFilePath) { | ||
let androidManifestContent; | ||
try { | ||
androidManifestContent = this.$fs.readFile(manifestFilePath).toString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use this.$fs.readText
here. This way you'll not have to use toString()
} | ||
} | ||
|
||
private validateOptions(options: IBuildOptions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method needs return type
} | ||
|
||
// Do nothing, the Android Gradle script will configure itself based on the input dependencies.json | ||
} | ||
|
||
public async checkIfPluginsNeedBuild(projectData: IProjectData): Promise<Array<any>> { | ||
const detectedPlugins: Array<any> = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can introduce an interface in this class or at least inline the properties:
public async checkIfPluginsNeedBuild(projectData: IProjectData): Promise<Array<{ platformsAndroidDirPath: string, pluginName: string }>> {
const detectedPlugins: Array<{ platformsAndroidDirPath: string, pluginName: string }> = [];
return detectedPlugins; | ||
} | ||
|
||
private isAllowedFile(item: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method needs return type
const currentItemStat = this.$fs.getFsStats(item); | ||
if (currentItemStat.mtime > aarStat.mtime) { | ||
detectedPlugins.push({ | ||
platformsAndroidDirPath: platformsAndroidDirPath, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use the short syntax here:
detectedPlugins.push({
platformsAndroidDirPath,
pluginName
});
}); | ||
} else if (nativeFiles.length > 0) { | ||
detectedPlugins.push({ | ||
platformsAndroidDirPath: platformsAndroidDirPath, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above:
you can use the short syntax here:
detectedPlugins.push({
platformsAndroidDirPath,
pluginName
});
import * as path from "path"; | ||
import * as shell from "shelljs"; | ||
|
||
const xml2js = require("xml2js"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to add a separate service that is wrapper for xml2js
package and to inject the new service in constructor. (It's not a merge stopper)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO we can hold off on that because xml2js
is used only in AndroidPluginBuildService
and there's no publicly exposed API, that can be used when the CLI is required.
return resultArr; | ||
} | ||
|
||
private getManifest(platformsDir: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we need to traverse the platforms dir to find the manifest
file. Shoudn't it be in platforms\android
folder. If this is the case, should'n we just return
path.join(platfromsDir, "android", AndroidPluginBuildService.ANDROID_MANIFEST_XML);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The plugin's platforms/android
folder does not necessarily have an AndroidManifest.xml
file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The migration tool started initially with the thought that it could also support flavors of the android libraries, in which cases there could be more than one manifests. Since I later realized that is a somewhat unrealistic expectation (that plugins would have additional flavors), I dropped the idea, but didn't modify the code as it kept working.
Yes, you can indeed use path.join followed by a check if fs.existsSync()
|
||
const xml2js = require("xml2js"); | ||
|
||
export class AndroidPluginBuildService implements IAndroidPluginBuildService { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider to add some unit tests for this service.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do
e620e83
to
9e44a11
Compare
4ecee82
to
149bdb2
Compare
Added unit tests and rebased on master branch. |
private $hostInfo: IHostInfo, | ||
private $androidToolsInfo: IAndroidToolsInfo) { } | ||
|
||
private static ANDROID_MANIFEST_XML = "AndroidManifest.xml"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use the MANIFEST_FILE_NAME constant instead
private $androidToolsInfo: IAndroidToolsInfo) { } | ||
|
||
private static ANDROID_MANIFEST_XML = "AndroidManifest.xml"; | ||
private static INCLUDE_GRADLE = "include.gradle"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as the include.gradle
string is already used in android-project-service.ts, maybe it is better to extract a constant in constants file and use it here and in AndroidProjectService
private static ANDROID_PLUGIN_GRADLE_TEMPLATE = "../../vendor/gradle-plugin"; | ||
|
||
private getAndroidSourceDirectories(source: string): Array<string> { | ||
const directories = ["res", "java", "assets", "jniLibs"]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use RESOURCES_DIR and ASSETS_DIR from constants
|
||
private async updateManifestContent(oldManifestContent: string, defaultPackageName: string): Promise<string> { | ||
const content = oldManifestContent; | ||
let newManifestContent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need to declare the variable here. Just declare it where you will use it.
} | ||
|
||
private async updateManifestContent(oldManifestContent: string, defaultPackageName: string): Promise<string> { | ||
const content = oldManifestContent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you need this variable?
package.json
Outdated
@@ -79,6 +79,7 @@ | |||
"winreg": "0.0.17", | |||
"ws": "2.2.0", | |||
"xcode": "https://github.com/NativeScript/node-xcode/archive/1.4.0.tar.gz", | |||
"xml2js": "^0.4.19", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use strict versions of both new dependencies.
test/ios-entitlements-service.ts
Outdated
@@ -47,7 +47,7 @@ describe("IOSEntitlements Service Tests", () => { | |||
injector = createTestInjector(); | |||
|
|||
platformsData = injector.resolve("platformsData"); | |||
projectData = injector.resolve("projectData"); | |||
projectData = $injector.resolve<IProjectData>("projectData"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this way you'll not use the local injector
variable, but the global $injector
one. It is a bad practice to use the global variable in the tests. Can you elaborate why you needed to change this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably a copy-paste error, but thanks for the explanation
@@ -32,20 +32,6 @@ describe("Plugin preparation", () => { | |||
assert.deepEqual({}, pluginPrepare.preparedDependencies); | |||
}); | |||
|
|||
it("skips prepare if every plugin prepared", async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it mean that now we'll never skip prepare even if all plugins are okay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, if the JS part of the plugins is prepared we will skill prepare, but we will always check for changes in the native part of a plugin for changes that need rebuilding.
await androidBuildPluginService.buildAar(config); | ||
const hasGeneratedAar = fs.exists(path.join(tempFolder, expectedAarName)); | ||
|
||
assert.equal(hasGeneratedAar, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert.isTrue(hasGeneratedAar);
|
||
allprojects { | ||
repositories { | ||
jcenter() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't we add the google() repository as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need, because we decide what dependencies to use, and we don't have any dependencies that would use google()
run ci |
lib/commands/plugin/build-plugin.ts
Outdated
private $fs: IFileSystem, | ||
private $options: IOptions) { | ||
|
||
if (this.$options.path) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can shorten the whole syntax:
this.pluginProjectPath = path.resolve(this.$options.path || ".");
|
||
const tempAndroidProject = path.join(platformsAndroidPath, "android-project"); | ||
|
||
const options: IBuildOptions = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use short syntax here:
const options: IBuildOptions = {
aarOutputDir: platformsAndroidPath,
platformsAndroidDirPath,
pluginName,
tempPluginDirPath: tempAndroidProject
};
But this is just a preference, so definitely not a merge stopper
lib/commands/plugin/build-plugin.ts
Outdated
} | ||
|
||
public async canExecute(args: string[]): Promise<boolean> { | ||
if (!this.$fs.exists(path.join(this.pluginProjectPath, "platforms", "android"))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
platforms -> constants.PLATFORMS_DIR_NAME
@@ -0,0 +1,15 @@ | |||
|
|||
interface IBuildOptions extends IAndroidBuildOptions{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface does not add anything to the IAndroidBuildOptions
. What is the purpose of having it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In one of your previous comments, you mentioned the properties are android specific so I moved them to an android specific interface.
return buildOptions; | ||
} | ||
|
||
public getBuildOptions(configurationFilePath?: string): Array<string> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation. As I cannot think of a better name, lets keep the current one.
filesToSync.push(filePath); | ||
} else if (event === "unlink" || event === "unlinkDir") { | ||
filesToRemove.push(filePath); | ||
if (!isAllowedFinalFile(filePath)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I place .aar
file in App_Resources? In case yes, what should happen here - we'll not include the files in filesToSync and... we should rebuild the application? Will this happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't talked about placing aar files in App_Resources
so I wouldn't know what would happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rosen-vladimirov App_Resources/Android/libs (where aars can be placed) is watched separately and should force a rebuild
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rosen-vladimirov I checked it out and when you have an example.aar
file in the App_Resources
and the tns run android
or tns run android --syncAllFiles
is running and the example.aar
is changed, the changes are being ignored.
When the command is stopped and ran again, the example.aar
forces a rebuild.
ae61e5a
to
e5cf9a3
Compare
6d0786c
to
7e192f9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After green build
handle --syncAllFiles in node_modules pass the same gradle options to the buildAar plugin and the native project
…lFiles flag * removed invalid test * move gradlew plugin to vendor folder and add licese file * add unit test for android build plugin service
…d aars docs(plugin build command): add docs for the new command
7e192f9
to
1cda138
Compare
run ci |
Don't merge before: telerik/mobile-cli-lib#1055
this PR is based on branch:
kddimitrov/nsconfig-app-folder
problem
the CLI doesn't build aar files for the native plugins
solution
build aar when:
res
folder in plugins'platforms/android/
folder--syncAllFiles
flag is passed (and a change is made in plugins' platforms/android/` folderrelated issues
NativeScript/android#890
NativeScript/nativescript-plugin-seed#65