Extending Build Process
To build a platform plug-in a common editor plug-in format is required. For the basic structure of the plug-in, please refer to the First Extension documentation . To extend the build function, it is necessary to understand the overall process of the build. Please read the Introduction to the build process and FAQ guide documentation.
Quick start
Click Project -> New Build Extension in the menu bar of the editor, and select Global/Project to create a build extension package.
If selecting Global, the build extension will be applied to all Cocos Creator projects. The path of Global is:
Windows:
%USERPROFILE%\.CocosCreator\extensions
macOS:
$HOME/.CocosCreator/extensions
If selecting Project, this will apply the build extension to the specified Cocos Creator project. The path of Project is:
$Your project address/extensions
After the build extension is created, notice the generation path of the plugin in the Console. Click on the path to open the build extension package in the file manager of the operating system.
Before enabling the build extension, execute
npm install
in the directory to install some dependent @types modules to compile normally. The interface definition that comes with the editor has been generated under the @types folder in the root directory. Developer -> Export.d.ts from the menu bar of the editor shows the latest interface definitions.Click Extension -> Extension Manager in the menu bar of the editor to open the Extension Manager panel. Then select the Project/Global tab in the Extension Manager, and click the Refresh Icon button to see the build extension just added. Then click the Enable button on the right to run the plug-in normally.
After the build extension is enabled, open the Build panel, notice the expansion bar of the build extension plugin. Click Build to join the build process.
To modify the content of the built extension, directly modify the build extension package under the
extensions
directory, review thereadme.md
file in the build extension package directory for details. Find the corresponding build extension in the Extension Manager, and click the Reload icon button. The extension in the editor will re-run with the latest code and files.
Basic configuration process
To extend the build function of the plug-in, add the builder
field to the contributions
in package.json
, and the relative path configuration of the corresponding module can be passed to the specified platform in the field.
Example package.json
:
// package.json
{
"contributions": {
"builder": "./dist/builder"
}
}
Note: the
builder
field specifies the./dist/builder.js
start script is the compiled script, and the source file of the start script is located in./source/builder.ts
. To configure the start script, change it in the source file.
Start script configuration
The plugin entry configuration code example is shown below:
// builder.ts
// Allow external developers to replace parts of the build asset handler module. Please refer to the "Custom Texture Compression Processing" section below for details.
export const assetHandlers: string = './asset-handlers';
export const configs: IConfigs = {
'web-mobile': {
hooks: './hooks',
options: {
remoteAddress: {
label: 'i18n:xxx',
render: {
ui: 'ui-input',
attributes: {
placeholder: 'Enter remote address...',
},
},
// Validation rules, there are currently several commonly used validation rules built in, and the rules that need to be customized can be configured in the "verifyRuleMap" field
verifyRules: ['require', 'http'],
},
enterCocos: {
label: 'i18n:cocos-build-template.options.enterCocos',
description: 'i18n:cocos-build-template.options.enterCocos',
default: '',
render: {
// Please click "Developer -> UI Components" in the menu bar of the editor to view a list of all supported UI components.
ui: 'ui-input',
attributes: {
placeholder: 'i18n:cocos-build-template.options.enterCocos',
},
},
verifyRules: ['ruleTest']
}
},
verifyRuleMap: {
ruleTest: {
message: 'i18n:cocos-build-template.ruleTest_msg',
func(val, option) {
if (val === 'cocos') {
return true;
}
return false;
}
}
}
},
};
Please pay extra attention to the following points when writing start scripts:
The environment variables in different processes will be different. The start script will be loaded by the rendering process and the main process at the same time, do not use the editor interface that only exists in a single process in the start script.
There are two ways to configure the key of
config
:One is for a single platform configuration, and the key is filled in as platform plugin name (available in the editor menu bar Extensions -> Extension Manager -> Internal to view the platform plug-in name).
One is the configuration for all platforms, the key is filled in as
*
. These two configuration methods are mutually exclusive, please do not use them in the same build extension package.
Note: these two configuration methods are mutually exclusive, please do not use both in the same build extension package. Otherwise the configuration for a single platform (key value
platform build plugin name
) will overwrite the configuration for all platforms (key value*
).
Customizing Build Panel Options
There are currently two ways to add build options to the interface: options automatic rendering configuration method and panel custom panel configuration method (>=3.8.2). The former automatically renders the content of the options configuration in the build panel and performs data update operations after some operations, while the latter adds a panel configuration to place the content of a custom panel, and the build will use this configuration to render with ui-panel
. To facilitate testing, this article will use web-mobile
as an example to demonstrate how to add new options to the build panel and display them.
Options Automatic Rendering Configuration Method
Create a new src/builder.ts
script file and write the following code in builder.ts
:
import { BuildPlugin, IBuildTaskOption } from "../@types/packages/builder/@types";
export const load: BuildPlugin.load = function() {
console.debug('custom-build-example load');
};
export const unload: BuildPlugin.load = function() {
console.debug('custom-build-example unload');
};
export const configs:BuildPlugin.Configs = {
'web-mobile': {
options: {
testInput: {
label: 'testVar',
description: 'this is a test input.',
default: '',
render: {
ui: 'ui-input',
attributes: {
placeholder: 'Enter numbers',
},
},
verifyRules: ['required','ruleTest']
},
testCheckbox: {
label: 'testCheckbox',
description: 'this is a test checkbox.',
default: false,
render: {
ui: 'ui-checkbox',
},
},
},
verifyRuleMap: {
ruleTest: {
message: 'length of content should be less than 6.',
func(val: any, option: IBuildTaskOption) {
if (val.length < 6) {
return true;
}
return false;
}
}
}
},
};
In the above configs
, we define two parameters:
testInput
: string - String variable, modified by input boxtestCheckbox
: boolean - Boolean variable, modified by checkbox
The meanings of each field in the configuration of build options are as follows:
label
: string - Required, the name of this parameter displayed on the interface, supportsi18n:key
configurationdescription
: string - Optional, brief description information, used for displaying hints when the mouse hovers over the label, supportsi18n:key
configurationdefault
: any - Optional, the default value of the parameterrender
: {} - Required, configure information about the rendered componentui
: string - Required, UI component name, please refer to the documentation UI componentsattributes
: {} - Optional, properties required by the UI component, please refer to the documentation UI components
verifyRules
: [] - Optional, parameter verification rulesverifyRuleMap
: [] - Optional, custom parameter verification rule functions. Refer to the section below on parameter validation rules
After executing npm run build
to compile this extension and refresh it, open the build panel and you can see two additional build parameters at the end of the web-mobile build task, as shown in the following figure:
Panel customization method (>=3.8.2)
Create a new src/builder.ts
script file and write the following code in builder.ts
:
import { BuildPlugin, IBuildTaskOption } from "../@types/packages/builder/@types";
export const configs:BuildPlugin.Configs = {
'web-mobile': {
panel: './panel',
}
};
Create a new src/panel.ts
script file and write the following code in panel.ts
:
import { ICustomPanelThis, ITaskOptions } from '../@types';
import { PACKAGE_NAME } from './global';
let panel: ICustomPanelThis;
export const style = ``;
export const template = `
<div class="build-plugin">
<ui-prop>
<ui-label slot="label" value="Hide Link"></ui-label>
<ui-checkbox slot="content"></ui-checkbox>
</ui-prop>
</div>
`;
export const $ = {
root: '.build-plugin',
hideLink: 'ui-checkbox',
link: '#link',
};
/**
* all change of options dispatched will enter here
* @param options
* @param key
* @returns
*/
export async function update(options: ITaskOptions, key: string) {
if (key) {
return;
}
// when import build options, key will bey ''
init();
}
export function ready(options: ITaskOptions) {
// @ts-ignore
panel = this as ICustomPanelThis;
panel.options = options;
init();
}
export function close() {
panel.$.hideLink.removeEventListener('change', onHideLinkChange);
}
function init() {
panel.$.hideLink.value = panel.options.hideLink;
updateLink();
panel.$.hideLink.addEventListener('change', onHideLinkChange);
}
function onHideLinkChange(event: any) {
panel.options.hideLink = event.target.value;
// Note: dispatch the change to build panel
panel.dispatch('update', `packages.${PACKAGE_NAME}.hideLink`, panel.options.hideLink);
updateLink();
}
function updateLink() {
if (panel.options.hideLink) {
panel.$.link.style.display = 'none';
} else {
panel.$.link.style.display = 'block';
}
}
Rules for customizing build panels
The above code comes from a simple example code in the template of a custom build plugin. You can choose other front-end frameworks to develop the interface more conveniently according to your preferences. Just pay attention to the following rules:
The file content rules of the panel are rendered by ui-panel, and need to follow the relevant component rules, which basically refer to the variables or lifecycle hooks exposed in the above example code.
The
ready
function of the panel will be called after the panel has been loaded. According to needs, you can do some dom element initialization, event binding, or some front-end framework initialization binding in this function.The
close
function of the panel will be called when the panel is closed. According to needs, you can do some event unregistration, object destruction, etc. in this function.The
update
function of the panel will be called whenever any build option changes (including those sent by itself). If necessary, you can add this hook here. At the same time, when the build panel has an action of importing configuration, you can also do some updating of the customization panel in this function. At this time,key
is an empty string.If the interaction in the customized panel involves updating the build option, including the data update that may be done during the initialization process, you must use
panel.dispatch('update', key, value, error)
to perform the update, wherekey
needs to follow the format ofpackages.${PACKAGE_NAME}.key
. Only after calling it will the data be synchronized to the final build option when building. Sending an error mainly affects the disablement status of the build button, and error is an optional parameter.If the interaction in the customized panel involves reading the build option, you need to use
panel.options.key
Start script interface definition
The detailed interface definition is described as follows:
declare type IConfigs = Record<Platform | '*', IPlatformConfig>;
declare interface IBuildPlugin {
hooks?: string; // Storage path of hook function
options?: IDisplayOptions; // Platform parameter configuration that needs to be injected
verifyRuleMap?: IVerificationRuleMap; // Register parameter verification rule function
}
declare type IDisplayOptions = Record<string, IConfigItem>;
declare interface IConfigItem {
// The default value, the registered default value will be in the "options.[platform].xxx" field in the plugin configuration
default?: any;
render: ?{
// The rules for rendering UI components are consistent with the unified rules at "ui-prop". Only configurations with UI properties specified will be displayed on the Build panel
ui?: string;
// The configuration parameters passed to the UI component
attributes?: IUiOptions;
};
// Configure the displayed name, if you need to translate, then pass in "i18n:${key}"
label?: string;
// A brief description of the setting, which will be displayed on the title when the mouse hovers over the configuration name.
description?: string;
// Type of configuration
type?: 'array' | 'object';
// If type is an array, the data will be rendered according to the specified data type and "itemConfigs"
itemConfigs?: Record<string, IConfigItem> | IConfigItem[];
}
declare interface IUiOptions extends IOptionsBase {
// Validation rules array, build provides some basic rules, and you can also specify new validation rules through “verifyRuleMap”. Only when pass in “require” will be a valueless checksum, otherwise only when there is a value.
verifyRules?: string[];
}
declare interface IUiOptions extends IOptionsBase {
class?: string | string[]; // The name of the style that needs to be set on the current "ui-prop"
}
For the interface definition of IOptionsBase
please refer to ui-prop automatic rendering rule definition.
Custom build hook function code configuration
In the script module defined by the hooks field in the entry configuration, hook functions can be written that build the life cycle. In different hook functions, the data received will be different. All hook functions run in the build process, and the engine method can be used directly in the build process.
The relationship between the public hook function and the life cycle of the build can be seen in the following figure:
The rough interface definition of hook function is as follows:
declare interface IHook {
throwError?: boolean; // The hook function injected by the plugin, whether to exit the build process directly and show the build failure when the execution fails.
// ------------------ hook function --------------------------
onBeforeBuild?: IBaseHooks;
onBeforeCompressSettings?: IBaseHooks;
onAfterCompressSettings?: IBaseHooks;
onAfterBuild?: IBaseHooks;
// Compile the generated hook function (only valid if the platform's build process has a "Make" step)
onBeforeMake?: (root: string, options: IBuildTaskOptions) => void | Promise<void>;
onAfterMake?: (root: string, options: IBuildTaskOptions) => void | Promise<void>;
}
type IBaseHooks = (options: IBuildTaskOptions, result?: IBuildResult) => void | Promise<void>;
Notes:
- the
result
parameter can be accessed only at the beginning ofonBeforeCompressSettings
, and theoptions
passed to the hook function is a copy of theoptions
used in the actual build process, and only used as a reference for information acquisition, so directly modifying it does not really affect the build process, although it can be modified successfully. To modify the build parameters, please set in theoptions
field of the entry configuration code. Due to the numerous interface definitions, you can refer to the@types/packages/builder
folder in the build extension package for detailed interface definitions.- The hook function is allowed to be an asynchronous function, and the build will await the completion of the hook function execution before executing the next process by default.
A simple example:
export function onBeforeBuild(options) {
// Todo some thing...
}
export async function onBeforeCompressSettings(options, result) {
// Todo some thing...
}
Custom texture compression processing
The assetHandler
path configuration specified in the start script configuration above allows external developers to register some asset handling functions to replace the engine's handler module when building partial assets. Currently only texture compression handler registration is available.
Creator provides its own compression tools to handle compressed texture assets at build time, but does not focus on image compression because it needs to be compatible with different user environments and usually the compression tools are chosen to work on most computers rather than the most efficient ones. Therefore, Creator has opened up a plug-in mechanism in v3.4, which allows users to directly register compression processing functions for the corresponding texture assets, which will be called at the appropriate processing time when building.
The specific steps are as follows:
In the start script, write the relative path of the
assetHandlers
module script:tsexport const assetHandlers = './asset-handlers';
In the
assetHandlers
script module, thecompressTextures
function has been opened up for developers to write the corresponding handler function directly incompressTextures
, which will be called during the texture compression phase of the build.The handler function takes the current array of remaining unprocessed texture compression tasks and removes them from the original array when processing is complete. Texture compression tasks that are not removed are considered unprocessed and are placed in the next corresponding processing function until all processing functions have been processed, and if there are still unprocessed texture compression tasks, they are placed back in the Creator's original texture compression process.
When there are multiple plugins registered with texture compression handler functions, they are executed in the order in which the plugins are started. If the previous plugin processes all the texture compression tasks, the subsequent plugins registered with the handler functions will not receive the tasks.
The code example is as follows:
tstype ITextureCompressType = | 'jpg' | 'png' | 'webp' | 'pvrtc_4bits_rgb' | 'astc_12x12'; // See interface definition for detailed format interface ICompressTasks { src: string; // Source file address dest: string; // Address of the generated target file (default suffix is PNG, other types need to be changed manually) quality: number | IPVRQuality | IASTCQuality | IETCQuality; // Compression quality 0 - 100 or other compression levels format: ITextureCompressType; // Compression type } export async function compressTextures(tasks: ICompressTasks[]) { for (let i = 0; i < Array.from(tasks).length; i++) { const task = Array.from(tasks)[i]; if (task.format ! == 'jpg') { // Texture compression tasks that are skipped are passed to the next processing function until they finally enter the Creator's original build-time texture compression process continue; } task.dest = task.dest.replace('.png', '.jpg'); await pngToJPG(task.src, task.dest, task.quality); // Remove the finished texture compression task from tasks, so that it will not be processed again when building tasks.split(i, 1); } }
Debugging build extension plugins
When the build extension plugin is involved in the build process, the associated code runs in the following three processes:
- Main Process: executes the start script and its dependent assets.
- Rendering Process: executes some of the fields registered in the start script to the Build panel.
- Build Process: executes the script defined in the
hooks
field of the start script.
Main Process (Start Script)
The main process mainly executes the start script used in the build extension plugin to participate in the build process (the script specified in the builder
field), and the plugin's own start script (the script specified in the main
field).
When the code running in the main process is modified, the plugin must be restarted and then the process to be updated must be refreshed (this will be optimized later to try to solve the code update problem with a single restart, but refreshing is still the most thorough reloading method). The main process currently does not have a more appropriate debugging method, you can use the command line to open the editor to view the main process code log to assist debugging:
// Mac
/Applications/CocosCreator/Creator/3.0.0/CocosCreator.app/Contents/MacOS/CocosCreator --project projectPath
// Windows
... \CocosCreator.exe --project projectPath
Rendering Process (Build Panel)
The start script of the build extension plugin has some fields that are registered to the Build panel, such as the display configuration of options
, the panel
field, and the panel
script itself, which is loaded and executed in the render process. The rendering process is actually the window's own execution process. Open the DevTools to debug the dom
elements, styles, scripts, etc. on the Build panel.
If the code registered to the Build panel is modified, refresh the panel without restarting the plugin.
Open the DevTools for the rendering process of the Build panel
Click on the Build panel and press the shortcut Ctrl + Shift + I (Windows) or Command + Option + I (Mac) to open the DevTools for the Build panel.
How to reload (refresh) the panel
Press Ctrl/Command + R after clicking on the Build panel or the DevTool in the Build panel.
Build Process (hooks
Script)
The actual execution phase of the build is a separate worker process, ensuring that even if an abnormal crash occurs, it will not affect the normal use of other windows. The scripts defined in the hooks
field of the start script are also loaded and executed in this separate worker process.
If only the script defined in the hook
field is modified, the build process can be refreshed without restarting the plugin. To do this, press Ctrl/Command + R after opening the build DevTools, as in the Build panel above.
Opening the DevTools for the build process
This includes the following three ways:
Click the Open Build DevTools button at the top right of the build task window in the Build panel.
Click Developers -> Open Build DevTools in the main editor menu to open it.
In any plugin code or in the console, execute the following code:
tsEditor.Message.send('builder', 'open-devtools');
Editor.Message
can be extended to suit additional needs. For example, developers can catch errors in the code written to build the plugin, and automatically open DevTools once there is an exception or something like that.