Skip to content

Commit

Permalink
add modularize js advisor and fix some typo (#59)
Browse files Browse the repository at this point in the history
* add modularize js advisor and fix some typo

* add test cases for modularize advisor

* Update license of src/advisors/modularize_js.ts

Co-authored-by: Lingyun Cai <[email protected]>

---------

Co-authored-by: Lingyun Cai <[email protected]>
  • Loading branch information
NingW101 and lingyuncai authored Jan 12, 2024
1 parent f32b797 commit 150bb47
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 10 deletions.
5 changes: 3 additions & 2 deletions src/advisor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class AdviseManager implements IAdviseManager {
queueRequest(req: IAdviseRequest) {
this._requestList.push(req);
}

async advise(): Promise<Recipe[]> {
let requests = this._requestList;
const recipes: Recipe[] = [];
Expand All @@ -47,7 +48,7 @@ export class AdviseManager implements IAdviseManager {
for (const advisor of pipeline.advisors) {
const result = await advisor.advise(this.proj, req, requests);
if (result.handled) {
log.info(` - adviced by ${advisor.type}`);
log.info(` - advised by ${advisor.type}`);
if (result.recipe) {
recipes.push(result.recipe);
}
Expand Down Expand Up @@ -89,7 +90,7 @@ export class AdviseManager implements IAdviseManager {
export function registerAdvisorFactory(type: string, factory: IAdvisorFactory) {
H.assert(
!_ALL_ADVISOR_FACTORIES.has(type),
`Already registerred AdvisorFactory with type: ${type}`
`Already registered AdvisorFactory with type: ${type}`
);

_ALL_ADVISOR_FACTORIES.set(type, factory);
Expand Down
6 changes: 4 additions & 2 deletions src/advisor_pipelines.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
{ "__type__": "HeaderMissingAdvisor" },
{ "__type__": "MakeTargetAdvisor" },
{ "__type__": "SDLAdvisor" },
{ "__type__": "TestTargetErrorAdvisor" }
{ "__type__": "TestTargetErrorAdvisor" },
{ "__type__": "ModularizeJSAdvisor" }
]
},
{
Expand All @@ -25,7 +26,8 @@
{ "__type__": "ExportNameAdvisor" },
{ "__type__": "MainLoopAdvisor" },
{ "__type__": "PThreadAdvisor" },
{ "__type__": "TemplateLiteralValidateAdvisor" }
{ "__type__": "TemplateLiteralValidateAdvisor" },
{ "__type__": "ModularizeJSAdvisor" }
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/advisors/dep_check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class DepCheckAdvisor implements IAdvisor {
actions.push(
new ShowSuggestionAction(
"option",
`We detect that your project depends on below requiured packages:\n${nonPorts.join(
`We detect that your project depends on below required packages:\n${nonPorts.join(
", "
)}\nHowever, we can't use native build versions of them. Instead, please build them from source with Webinizer first - add them to your dependent projects and then link them against the current main project.`,
null,
Expand Down
6 changes: 3 additions & 3 deletions src/advisors/make_target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ class MakeTargetAdvisor implements IAdvisor {
break;
}
}
// 3. check if the make directory is a auxilliaryDir that is not necessary to build
// 3. check if the make directory is a auxiliaryDir that is not necessary to build
// FIXME. extend the hardcode here to cover more cases
const auxilliaryDir = ["doc", "docs", "demo", "man", "test"];
if (target && dir && auxilliaryDir.includes(path.relative(path.resolve(proj.root), dir))) {
const auxiliaryDir = ["doc", "docs", "demo", "man", "test"];
if (target && dir && auxiliaryDir.includes(path.relative(path.resolve(proj.root), dir))) {
return this._generateMakeTargetAdvise(proj, errorReq, target, dir);
}
}
Expand Down
126 changes: 126 additions & 0 deletions src/advisors/modularize_js.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*-------------------------------------------------------------------------------------------------
* Copyright (C) 2024 Intel Corporation. All rights reserved.
* Licensed under the Apache License 2.0. See LICENSE in the project root for license information.
* SPDX-License-Identifier: Apache-2.0
*-----------------------------------------------------------------------------------------------*/

import path from "path";
import fs from "graceful-fs";
import { registerAdvisorFactory } from "../advisor";
import { Recipe } from "../recipe";
import { PlainAdviseRequest, ErrorAdviseRequest } from "../advise_requests/common_requests";
import { ConfigOptionChangeAction } from "../actions/config_option_change";
import {
IAdviseRequest,
IAdviseResult,
IAdvisorFactory,
IAdvisor,
Project as IProject,
} from "webinizer";
import { isPrevBuildersAllNative, findFirstBuilder } from "../builder";

class ModularizeJSAdvisorFactory implements IAdvisorFactory {
name = "ModularizeJSAdvisorFactory";
desc = "Use this factory class to create ModularizeJSAdvisor instance";

createAdvisor(): IAdvisor {
return new ModularizeJSAdvisor();
}
}

class ModularizeJSAdvisor implements IAdvisor {
static __type__ = "ModularizeJSAdvisor";
type = ModularizeJSAdvisor.__type__;
desc = "Advise issues related to modularize JS output";

private async _generateModularizeJSAdvise(
proj: IProject,
req: ErrorAdviseRequest | PlainAdviseRequest
): Promise<IAdviseResult> {
// set option 'needModularize' to false
const action = new ConfigOptionChangeAction(
proj,
"We have detected that `html` is set as the build target format, which conflicts with the modularize js output configuration option. Do you want to disable the `Modularize JS output` option and keep html as the build target format?",
{ needModularize: false }
);

return {
handled: true,
recipe: new Recipe(proj, "Recipe for modularize JS output option", this, req, action),
};
}

private async _checkTargetFormat4PreBuild(proj: IProject): Promise<boolean> {
// pre-build check
const rawBuilders = proj.config.getBuildConfigForTarget(proj.config.target).rawBuilders;
// FIXME. currently we only support checking for CMake file
if (
rawBuilders &&
((rawBuilders[0].__type__ as string) === "CMakeBuilder" ||
isPrevBuildersAllNative(proj, findFirstBuilder(proj, "CMakeBuilder")))
) {
const workingDir = (
rawBuilders[findFirstBuilder(proj, "CMakeBuilder")].rootBuildFilePath as string
).replace("${projectRoot}", proj.root);

const suffixReg = /set\(CMAKE_EXECUTABLE_SUFFIX ".html"\)/g;
const file = path.join(workingDir, "CMakeLists.txt");
const relative = path.relative(proj.root, file);

if (
fs.existsSync(file) &&
relative &&
!relative.startsWith("..") &&
!path.isAbsolute(relative)
) {
const lines = fs.readFileSync(file, "utf-8").split("\n");
for (let i = 0; i < lines.length; i++) {
if (lines[i].trim().length > 0 && !lines[i].trim().startsWith("#")) {
const m = lines[i].match(suffixReg);
if (m !== null) return true;
}
}
}
}
return false;
}

/* eslint-disable @typescript-eslint/no-unused-vars */
async advise(
proj: IProject,
req: IAdviseRequest,
requestList: ReadonlyArray<IAdviseRequest> // one can only return newRequestQueue to change it
): Promise<IAdviseResult> {
if (req instanceof PlainAdviseRequest) {
// pre-build check
const plainReq = req as PlainAdviseRequest;
// check if html is set as the build target format in cmake file
if (
(await this._checkTargetFormat4PreBuild(proj)) &&
proj.config.getBuildConfigForTarget(proj.config.target).getOption("needModularize")
) {
return this._generateModularizeJSAdvise(proj, plainReq);
}
}

if (req instanceof ErrorAdviseRequest) {
// build check
const errorReq = req as ErrorAdviseRequest;
const errorReg =
/.+emsdk\/upstream\/emscripten\/src\/shell\.html" is not compatible with build options "-sMODULARIZE -sEXPORT_NAME=Module"/g;
const m = errorReq.error.match(errorReg);
if (m !== null) {
return this._generateModularizeJSAdvise(proj, errorReq);
}
}

return {
handled: false,
};
}
}

// loading
export default function onload() {
registerAdvisorFactory(ModularizeJSAdvisor.__type__, new ModularizeJSAdvisorFactory());
}
2 changes: 1 addition & 1 deletion src/json_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function checkJsonType(type: string, o: IJsonObject) {
}

// By default, the Factory only contains fromJson(), but if one need to add more
// items into it, you could explictly pass in X as well.
// items into it, you could explicitly pass in X as well.
export class JsonFactories<
T,
X extends IFromJson<T> | FromJsonMethod<T> = IFromJson<T> | FromJsonMethod<T>
Expand Down
25 changes: 24 additions & 1 deletion tests/advisor_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ describe("advisor", () => {

it("DepCheckAdvisorTest", async () => {
const req = new PlainAdviseRequest("pre-build", "");
const actionDesc = "We detect that your project depends on below requiured packages:";
const actionDesc = "We detect that your project depends on below required packages:";
const advisorType = "DepCheckAdvisor";
const projRoot = path.join(TEST_ADVISOR_ASSETS_DIR, advisorType);
const result = await advise(advisorType, projRoot, req);
Expand Down Expand Up @@ -403,4 +403,27 @@ describe("advisor", () => {
expect(result.handled).to.equal(true);
expect((result.recipe as Recipe).actions[0].desc).to.include(actionDesc);
});

it("ModularizeJSAdvisorTest1", async () => {
const errMsg = `"/emsdk/upstream/emscripten/src/shell.html" is not compatible with build options "-sMODULARIZE -sEXPORT_NAME=Module"`;
const req = new ErrorAdviseRequest("make", errMsg, null, 0);
const actionDesc = `We have detected that \`html\` is set as the build target format, which conflicts with the modularize js output configuration option. Do you want to disable the \`Modularize JS output\` option and keep html as the build target format?`;
const advisorType = "ModularizeJSAdvisor";
const projRoot = path.join(TEST_ADVISOR_ASSETS_DIR, advisorType);
const result = await advise(advisorType, projRoot, req);

expect(result.handled).to.equal(true);
expect((result.recipe as Recipe).actions[0].desc).to.include(actionDesc);
});

it("ModularizeJSAdvisorTest2", async () => {
const req = new PlainAdviseRequest("pre-build", "");
const actionDesc = `We have detected that \`html\` is set as the build target format, which conflicts with the modularize js output configuration option. Do you want to disable the \`Modularize JS output\` option and keep html as the build target format?`;
const advisorType = "ModularizeJSAdvisor";
const projRoot = path.join(TEST_ADVISOR_ASSETS_DIR, advisorType);
const result = await advise(advisorType, projRoot, req);

expect(result.handled).to.equal(true);
expect((result.recipe as Recipe).actions[0].desc).to.include(actionDesc);
});
});
31 changes: 31 additions & 0 deletions tests/assets/advisors/ModularizeJSAdvisor/.webinizer/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"__type__": "ProjectConfig",
"name": "ModularizeJSAdvisorTest",
"desc": "Test config file for ModularizeJSAdvisor",
"version": "1.0.0",
"buildTargets": {
"static": {
"options": {
"needMainLoop": true,
"needPthread": false,
"needCppException": false,
"needSimd": true,
"needModularize": true
},
"envs": {
"cflags": "-msimd128",
"ldflags": "-msimd128 -sMODULARIZE=1"
},
"builders": [
{
"__type__": "CMakeBuilder",
"id": 0,
"desc": "CMake",
"args": "",
"rootBuildFilePath": "${projectRoot}"
}
]
}
},
"target": "static"
}
4 changes: 4 additions & 0 deletions tests/assets/advisors/ModularizeJSAdvisor/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.12)
project(MyProject)

set(CMAKE_EXECUTABLE_SUFFIX ".html")

0 comments on commit 150bb47

Please sign in to comment.