Skip to content

feat: build transient native dependencies #5643

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

Merged
merged 3 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 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
Loading