Skip to content

Commit

Permalink
feat: build transient native dependencies (#5643)
Browse files Browse the repository at this point in the history

Co-authored-by: Igor Randjelovic <rigor789@gmail.com>
  • Loading branch information
farfromrefug and rigor789 committed Mar 7, 2022
1 parent 356630a commit 1435eef
Show file tree
Hide file tree
Showing 12 changed files with 325 additions and 133 deletions.
6 changes: 3 additions & 3 deletions lib/definitions/android-plugin-migrator.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ interface IBuildAndroidPluginData extends Partial<IProjectDir> {
* Optional custom Gradle path.
*/
gradlePath?: string;
/**

/**
* Optional custom Gradle arguments.
*/
gradleArgs?: string,
gradleArgs?: string;
}
9 changes: 6 additions & 3 deletions lib/services/android-plugin-build-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
await this.setupGradle(
pluginTempDir,
options.platformsAndroidDirPath,
options.projectDir
options.projectDir,
options.pluginName
);
await this.buildPlugin({
gradlePath: options.gradlePath,
Expand Down Expand Up @@ -398,7 +399,8 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
private async setupGradle(
pluginTempDir: string,
platformsAndroidDirPath: string,
projectDir: string
projectDir: string,
pluginName: string
): Promise<void> {
const gradleTemplatePath = path.resolve(
path.join(__dirname, "../../vendor/gradle-plugin")
Expand All @@ -419,6 +421,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
buildGradlePath,
runtimeGradleVersions.gradleAndroidPluginVersion
);
this.replaceFileContent(buildGradlePath, "{{pluginName}}", pluginName);
}

private async getRuntimeGradleVersions(
Expand Down Expand Up @@ -729,7 +732,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
`-PcompileSdk=android-${pluginBuildSettings.androidToolsInfo.compileSdkVersion}`,
`-PbuildToolsVersion=${pluginBuildSettings.androidToolsInfo.buildToolsVersion}`,
`-PappPath=${this.$projectData.getAppDirectoryPath()}`,
`-PappResourcesPath=${this.$projectData.getAppResourcesDirectoryPath()}`
`-PappResourcesPath=${this.$projectData.getAppResourcesDirectoryPath()}`,
];
if (pluginBuildSettings.gradleArgs) {
localArgs.push(pluginBuildSettings.gradleArgs);
Expand Down
107 changes: 97 additions & 10 deletions lib/services/android-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,71 @@ import { IInjector } from "../common/definitions/yok";
import { injector } from "../common/yok";
import { INotConfiguredEnvOptions } from "../common/definitions/commands";

interface NativeDependency {
name: string;
directory: string;
dependencies: string[];
}

//
// we sort the native dependencies topologically to make sure they are processed in the right order
// native dependenciess need to be sorted so the deepst dependencies are built before it's parents
//
// for example, given this dep structure (assuming these are all native dependencies that need to be built)
// |- dep1
// |- dep2
// |- dep3
// |- dep4
// |-dep5
// |- dep6
//
// It is sorted:
// |- dep1 - doesn't depend on anything, so the order stays the same as in the input list
// |- dep3 - doesn't depend on anything, so the order stays the same as in the input list
// |- dep5 - doesn't depend on anything, so the order stays the same as in the input list
// |- dep6 - doesn't depend on anything, so the order stays the same as in the input list
// |- dep4 - depends on dep6, so dep6 must be built first, ie above ^
// |- dep2 - depends on dep3, dep4, dep5 and dep6, so all of them must be built first
//
// for more details see: https://wikiless.org/wiki/Topological_sorting?lang=en
//
function topologicalSortNativeDependencies(
nativeDeps: NativeDependency[],
start: NativeDependency[] = [],
depth = 0
): NativeDependency[] {
const processedDeps = nativeDeps.reduce(
(accumulator, nativeDep: NativeDependency) => {
if (
nativeDep.dependencies.every(
Array.prototype.includes,
accumulator.map((n) => n.name)
)
) {
accumulator.push(nativeDep);
}
return accumulator;
},
start
);

const remainingDeps = nativeDeps.filter(
(nativeDep) => !processedDeps.includes(nativeDep)
);

// recurse if we still have unprocessed deps
// the second condition here prevents infinite recursion
if (remainingDeps.length && depth <= nativeDeps.length) {
return topologicalSortNativeDependencies(
remainingDeps,
processedDeps,
depth + 1
);
}

return processedDeps;
}

export class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase {
private static VALUES_DIRNAME = "values";
private static VALUES_VERSION_DIRNAME_PREFIX =
Expand Down Expand Up @@ -635,10 +700,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
public async beforePrepareAllPlugins(
projectData: IProjectData,
dependencies?: IDependencyData[]
): Promise<void> {
): Promise<IDependencyData[]> {
if (dependencies) {
dependencies = this.filterUniqueDependencies(dependencies);
this.provideDependenciesJson(projectData, dependencies);
return this.provideDependenciesJson(projectData, dependencies);
}
}

Expand Down Expand Up @@ -666,7 +731,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
private provideDependenciesJson(
projectData: IProjectData,
dependencies: IDependencyData[]
): void {
): IDependencyData[] {
const platformDir = path.join(
projectData.platformsDir,
AndroidProjectService.ANDROID_PLATFORM_NAME
Expand All @@ -675,15 +740,37 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
platformDir,
constants.DEPENDENCIES_JSON_NAME
);
const nativeDependencies = dependencies
.filter(AndroidProjectService.isNativeAndroidDependency)
.map(({ name, directory }) => ({
name,
directory: path.relative(platformDir, directory),
}));
const jsonContent = JSON.stringify(nativeDependencies, null, 4);
let nativeDependencyData = dependencies.filter(
AndroidProjectService.isNativeAndroidDependency
);

let nativeDependencies = nativeDependencyData.map(
({ name, directory, dependencies }) => {
return {
name,
directory: path.relative(platformDir, directory),
dependencies: dependencies.filter((dep) => {
// filter out transient dependencies that don't have native dependencies
return (
nativeDependencyData.findIndex(
(nativeDep) => nativeDep.name === dep
) !== -1
);
}),
} as NativeDependency;
}
);
nativeDependencies = topologicalSortNativeDependencies(nativeDependencies);
const jsonContent = JSON.stringify(nativeDependencies, null, 4);
this.$fs.writeFile(dependenciesJsonPath, jsonContent);

// we sort all the dependencies to respect the topological sorting of the native dependencies
return dependencies.sort(function (a, b) {
return (
nativeDependencies.findIndex((n) => n.name === a.name) -
nativeDependencies.findIndex((n) => n.name === b.name)
);
});
}

private static isNativeAndroidDependency({
Expand Down
21 changes: 17 additions & 4 deletions lib/services/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import { IOSProvisionService } from "./ios-provision-service";
import { IOSEntitlementsService } from "./ios-entitlements-service";
import { IOSBuildData } from "../data/build-data";
import { IOSPrepareData } from "../data/prepare-data";
import { BUILD_XCCONFIG_FILE_NAME, CONFIG_FILE_NAME_DISPLAY, IosProjectConstants } from "../constants";
import {
BUILD_XCCONFIG_FILE_NAME,
CONFIG_FILE_NAME_DISPLAY,
IosProjectConstants,
} from "../constants";
import { hook } from "../common/helpers";
import {
IPlatformData,
Expand All @@ -29,8 +33,14 @@ import {
IIOSNativeTargetService,
IValidatePlatformOutput,
} from "../definitions/project";

import { IBuildData } from "../definitions/build";
import { IXcprojService, IXcconfigService, IOptions } from "../declarations";
import {
IXcprojService,
IXcconfigService,
IDependencyData,
IOptions,
} from "../declarations";
import { IPluginData, IPluginsService } from "../definitions/plugins";
import {
IFileSystem,
Expand Down Expand Up @@ -976,8 +986,11 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
await this.addExtensions(projectData, pluginsData);
}

public beforePrepareAllPlugins(): Promise<void> {
return Promise.resolve();
public beforePrepareAllPlugins(
projectData: IProjectData,
dependencies?: IDependencyData[]
): Promise<IDependencyData[]> {
return Promise.resolve(dependencies);
}

public async checkForChanges(
Expand Down
5 changes: 2 additions & 3 deletions lib/services/platform-environment-requirements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ export class PlatformEnvironmentRequirements
// private $staticConfig: IStaticConfig,
private $analyticsService: IAnalyticsService,
// @ts-ignore - required by the hook helper!
private $injector: IInjector
) // private $previewQrCodeService: IPreviewQrCodeService
{}
private $injector: IInjector // private $previewQrCodeService: IPreviewQrCodeService
) {}

// public get $previewAppController(): IPreviewAppController {
// return this.$injector.resolve("previewAppController");
Expand Down
5 changes: 4 additions & 1 deletion lib/services/project-changes-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ export class ProjectChangesService implements IProjectChangesService {
);

this.$nodeModulesDependenciesBuilder
.getProductionDependencies(projectData.projectDir, projectData.ignoredDependencies)
.getProductionDependencies(
projectData.projectDir,
projectData.ignoredDependencies
)
.filter(
(dep) =>
dep.nativescript &&
Expand Down
2 changes: 1 addition & 1 deletion lib/services/webpack/webpack.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ declare global {
beforePrepareAllPlugins(
projectData: IProjectData,
dependencies?: IDependencyData[]
): Promise<void>;
): Promise<IDependencyData[]>;

handleNativeDependenciesChange(
projectData: IProjectData,
Expand Down
4 changes: 2 additions & 2 deletions lib/tools/node-modules/node-modules-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
platformData,
projectData,
}: IPrepareNodeModulesData): Promise<void> {
const dependencies = this.$nodeModulesDependenciesBuilder.getProductionDependencies(
let dependencies = this.$nodeModulesDependenciesBuilder.getProductionDependencies(
projectData.projectDir, projectData.ignoredDependencies
);
await platformData.platformProjectService.beforePrepareAllPlugins(
dependencies = await platformData.platformProjectService.beforePrepareAllPlugins(
projectData,
dependencies
);
Expand Down
29 changes: 22 additions & 7 deletions lib/tools/node-modules/node-modules-dependencies-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { IDependencyData } from "../../declarations";
import { IFileSystem } from "../../common/declarations";
import * as _ from "lodash";
import { injector } from "../../common/yok";
import { resolvePackagePath } from "@rigor789/resolve-package-path";

interface IDependencyDescription {
parent: IDependencyDescription;
Expand Down Expand Up @@ -43,7 +44,8 @@ export class NodeModulesDependenciesBuilder
const currentModule = queue.shift();
const resolvedDependency = this.findModule(
currentModule,
resolvedDependencies
resolvedDependencies,
projectPath
);

if (
Expand Down Expand Up @@ -86,16 +88,29 @@ export class NodeModulesDependenciesBuilder

private findModule(
depDescription: IDependencyDescription,
resolvedDependencies: IDependencyData[]
resolvedDependencies: IDependencyData[],
rootPath: string
): IDependencyData {
try {
const parentModulesPath =
depDescription?.parentDir ?? depDescription?.parent?.parentDir;
const modulePath = require
.resolve(`${depDescription.name}/package.json`, {
paths: [parentModulesPath],
})
.replace(/[\\/]+package\.json$/, "");

let modulePath: string = resolvePackagePath(depDescription.name, {
paths: [parentModulesPath],
});

// perhaps traverse up the tree here?
if (!modulePath) {
// fallback to searching in the root path
modulePath = resolvePackagePath(depDescription.name, {
paths: [rootPath],
});
}

// if we failed to find the module...
if (!modulePath) {
return null;
}

// if we already resolved this dependency, we return null to avoid a duplicate resolution
if (
Expand Down

0 comments on commit 1435eef

Please sign in to comment.