lib/qx/tool/cli/commands/MConfig.js
async function() {
      var parsedArgs = await this.__parseImpl();
      var config = {};
      var contribConfig = {
        version: qx.tool.cli.ConfigSchemas.lockfile.version
      };

      if (parsedArgs.config) {
        config = await this.__loadJson(parsedArgs.config);
        let lockfile = qx.tool.cli.ConfigSchemas.lockfile.filename;
        try {
          var name = path.join(path.dirname(parsedArgs.config), lockfile);
          contribConfig = await this.__loadJson(name);
        } catch (ex) {
          // Nothing
        }
        // check semver-type compatibility (i.e. compatible as long as major version stays the same)
        let schemaVersion = semver.coerce(qx.tool.cli.ConfigSchemas.lockfile.version, true).raw;
        let fileVersion = contribConfig.version ? semver.coerce(contribConfig.version, true).raw : "1.0.0";
        if (semver.major(schemaVersion) > semver.major(fileVersion)) {
          if (this.argv.force) {
            let config = {
              verbose: this.argv.verbose,
              quite: this.argv.quiet,
              save: false
            };
            const installer = new qx.tool.cli.commands.contrib.Install(config);
            let filepath = installer.getContribFileName();
            let backup = filepath + ".old";
            await fs.copyFileAsync(filepath, backup);
            if (!this.argv.quiet) {
              console.info(`A backup of ${lockfile} has been saved to ${backup}, in case you need to revert to it.`);
            }
            await installer.deleteContribData();
            for (let lib of contribConfig.libraries) {
              if (!await installer.isInstalled(lib.uri, lib.repo_tag)) {
                if (lib.repo_tag) {
                  await installer.install(lib.uri, lib.repo_tag);
                } else if (lib.path && fs.existsSync(lib.path)) {
                  await installer.installFromLocaPath(lib.path, lib.uri);
                }
              } else if (this.argv.verbose) {
                console.info(`>>> ${lib.uri}@${lib.repo_tag} is already installed.`);
              }
            }
            contribConfig = await installer.getContribData();
          } else {
            throw new qx.tool.cli.Utils.UserError(
              `*** Warning ***\n` +
              `The schema of '${lockfile}' has changed. Execute 'qx clean && qx compile --force' to delete and regenerate it.\n` +
              `You might have to re-apply manual modifications to '${lockfile}'.`
            );
          }
        }
      }
      this._mergeArguments(parsedArgs, config, contribConfig);
      if (config.libraries) {
        for (const aPath of config.libraries) {
          if (typeof aPath === "object" && typeof aPath.path === "string") {
            throw new Error("Configuration for libraries has changed - it is now an array of strings, each of which is a path to the directory containing Manifest.json.  Please run 'qx upgrade'");
          }
          await this.__loadJs(path.join(aPath, "compile.js"), config);
        }
      }
      return config;
    }
async function() {
      var parsedArgs = await this.__parseImpl();
      var config = {};
      var contribConfig = {
        version: qx.tool.cli.ConfigSchemas.lockfile.version
      };

      if (parsedArgs.config) {
        config = await this.__loadJson(parsedArgs.config);
        let lockfile = qx.tool.cli.ConfigSchemas.lockfile.filename;
        try {
          var name = path.join(path.dirname(parsedArgs.config), lockfile);
          contribConfig = await this.__loadJson(name);
        } catch (ex) {
          // Nothing
        }
        // check semver-type compatibility (i.e. compatible as long as major version stays the same)
        let schemaVersion = semver.coerce(qx.tool.cli.ConfigSchemas.lockfile.version, true).raw;
        let fileVersion = contribConfig.version ? semver.coerce(contribConfig.version, true).raw : "1.0.0";
        if (semver.major(schemaVersion) > semver.major(fileVersion)) {
          if (this.argv.force) {
            let config = {
              verbose: this.argv.verbose,
              quite: this.argv.quiet,
              save: false
            };
            const installer = new qx.tool.cli.commands.contrib.Install(config);
            let filepath = installer.getContribFileName();
            let backup = filepath + ".old";
            await fs.copyFileAsync(filepath, backup);
            if (!this.argv.quiet) {
              console.info(`A backup of ${lockfile} has been saved to ${backup}, in case you need to revert to it.`);
            }
            await installer.deleteContribData();
            for (let lib of contribConfig.libraries) {
              if (!await installer.isInstalled(lib.uri, lib.repo_tag)) {
                if (lib.repo_tag) {
                  await installer.install(lib.uri, lib.repo_tag);
                } else if (lib.path && fs.existsSync(lib.path)) {
                  await installer.installFromLocaPath(lib.path, lib.uri);
                }
              } else if (this.argv.verbose) {
                console.info(`>>> ${lib.uri}@${lib.repo_tag} is already installed.`);
              }
            }
            contribConfig = await installer.getContribData();
          } else {
            throw new qx.tool.cli.Utils.UserError(
              `*** Warning ***\n` +
              `The schema of '${lockfile}' has changed. Execute 'qx clean && qx compile --force' to delete and regenerate it.\n` +
              `You might have to re-apply manual modifications to '${lockfile}'.`
            );
          }
        }
      }
      this._mergeArguments(parsedArgs, config, contribConfig);
      if (config.libraries) {
        for (const aPath of config.libraries) {
          if (typeof aPath === "object" && typeof aPath.path === "string") {
            throw new Error("Configuration for libraries has changed - it is now an array of strings, each of which is a path to the directory containing Manifest.json.  Please run 'qx upgrade'");
          }
          await this.__loadJs(path.join(aPath, "compile.js"), config);
        }
      }
      return config;
    }
Parses the command line and produces configuration data

Loads a configuration file from a .js or .json file; if you provide a .js
file the file MUST return a function.
If there is also a .json, then it is loaded and parsed first.

The Function returned from a .js file MUST accept two arguments, one for the
data (which will be null if there is no .json) and the second is the callback
to call when complete; the callback takes an error object and the output
configuration data.

Configuration files do not support processes, job executions, or even
macros - if you want to add basic processing (eg for macros), use a .js file to
manipulate the data.  If you want to customise the Maker that is produced, you
need to use the API directly.