async function nwbuild(options) {
  let built;
  let releaseInfo = {};
  let manifest = {
    path: '',
    json: undefined,
  };

  try {
    /* Parse options */
    options = await util.parse(options, manifest);
    util.log('debug', 'info', 'Parse initial options');

    util.log('debug', 'info', 'Get node manifest...');
    manifest = await util.getNodeManifest({ srcDir: options.srcDir, glob: options.glob });
    if (typeof manifest.json?.nwbuild === 'object') {
      options = manifest.json.nwbuild;
    }

    util.log('info', options.logLevel, 'Parse final options using node manifest');
    options = await util.parse(options, manifest.json);
    util.log('debug', options.logLevel, 'Manifest: ', `${manifest.path}\n${manifest.json}\n`);

    built = fs.existsSync(options.cacheDir);
    if (built === false) {
      await fs.promises.mkdir(options.cacheDir, { recursive: true });
    }

    if (options.mode === 'build') {
      built = fs.existsSync(options.outDir);
      if (built === false) {
        await fs.promises.mkdir(options.outDir, { recursive: true });
      }
    }

    /* Validate options.version to get the version specific release info */
    util.log('info', options.logLevel, 'Get version specific release info...');
    releaseInfo = await util.getReleaseInfo(
      options.version,
      options.platform,
      options.arch,
      options.cacheDir,
      options.manifestUrl,
    );
    util.log('debug', options.logLevel, `Release info:\n${JSON.stringify(releaseInfo, null, 2)}\n`);

    util.log('info', options.logLevel, 'Validate options.* ...');
    await util.validate(options, releaseInfo);
    util.log('debug', options.logLevel, `Options:\n${JSON.stringify(options, null, 2)}`);

    /* Remove leading "v" from version string */
    options.version = releaseInfo.version.slice(1);

    util.log('info', options.logLevel, 'Getting NW.js and related binaries...');
    await get({
      version: options.version,
      flavor: options.flavor,
      platform: options.platform,
      arch: options.arch,
      downloadUrl: options.downloadUrl,
      cacheDir: options.cacheDir,
      cache: options.cache,
      ffmpeg: options.ffmpeg,
      nativeAddon: options.nativeAddon,
      shaSum: options.shaSum,
      logLevel: options.logLevel,
    });

    if (options.mode === 'get') {
      // Do nothing else since we have already downloaded the binaries.
      return undefined;
    }

    if (options.mode === 'run') {
      util.log('info', options.logLevel, 'Running NW.js in run mode...');
      const nwProcess = await run({
        version: options.version,
        flavor: options.flavor,
        platform: options.platform,
        arch: options.arch,
        srcDir: options.srcDir,
        cacheDir: options.cacheDir,
        glob: options.glob,
        argv: options.argv,
      });
      return nwProcess;
    } else if (options.mode === 'build') {
      util.log('info', options.logLevel, `Build a NW.js application for ${options.platform} ${options.arch}...`);
      await bld({
        version: options.version,
        flavor: options.flavor,
        platform: options.platform,
        arch: options.arch,
        manifestUrl: options.manifestUrl,
        srcDir: options.srcDir,
        cacheDir: options.cacheDir,
        outDir: options.outDir,
        app: options.app,
        glob: options.glob,
        managedManifest: options.managedManifest,
        nativeAddon: options.nativeAddon,
        zip: options.zip,
        releaseInfo: releaseInfo,
      });
      util.log('info', options.logLevel, `Appliction is available at ${path.resolve(options.outDir)}`);
    }
  } catch (error) {
    console.error(error);
    throw error;
  }

  return undefined;
}
Main module exported.
@async
@function
@param  {Options}       options  Options
@returns {Promise<child_process.ChildProcess | null | undefined>} - Returns NW.js process if run mode, otherwise returns `undefined`.