aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMark Miller <erights@gmail.com>2011-09-06 23:32:28 -0700
committerMark Miller <erights@gmail.com>2011-09-06 23:32:28 -0700
commit15586369294f850f4ce4099d1529a57960797e78 (patch)
tree0c2a8f5828aa244ad3a33a67e62655d912917b15 /tools
parent1f6f66cb196330290d890ac1e84481999b720e05 (diff)
Converts test cases to proposed new canonical form
Diffstat (limited to 'tools')
-rw-r--r--tools/converter/convert.js429
-rw-r--r--tools/converter/v8PosixPlatform.js275
2 files changed, 704 insertions, 0 deletions
diff --git a/tools/converter/convert.js b/tools/converter/convert.js
new file mode 100644
index 000000000..8a40fdc52
--- /dev/null
+++ b/tools/converter/convert.js
@@ -0,0 +1,429 @@
+
+(function(global) {
+ "use strict";
+
+ var t262 = global.t262;
+ var platform = t262.platform;
+ var regExp = platform.regExp;
+
+ var headerPattern = /(?:(?:\/\/.*)?\s*\n)*/;
+ var captureCommentPattern = /\/\*\*?((?:\s|\S)*?)\*\/\s*\n/;
+ var anyPattern = /(?:\s|\S)*/;
+ var blanksPattern = /(?:\s|\n)*/;
+
+ // Should match anything
+ var testEnvelopePattern =
+ regExp('^(', headerPattern,
+ ')(?:', captureCommentPattern,
+ ')?(', anyPattern,
+ ')$');
+
+ var registerPattern =
+ regExp('^(', anyPattern, '?)(',
+ /ES5Harness\.registerTest\s*\(\s*\{/, anyPattern,
+ /\}\s*\)/, ')',
+ /\s*;?(?:\s|\n)*$/);
+
+ var captureFuncBodyPattern =
+ regExp(/^function(?:\s+\w*)?\(\s*\)\s*\{/,
+ '(', anyPattern, ')',
+ /;?/, blanksPattern,
+ /\}$/);
+
+ var captureExprBodyPattern =
+ regExp(/^return\s+/,
+ '(', anyPattern, '?)',
+ /;$/);
+
+ var capturePredicatePattern =
+ regExp(/^if\s+\((.*?)\)\s*\{/, blanksPattern,
+ /return\s+true;?/, blanksPattern,
+ /\}$/);
+
+ /**
+ * Strip the left margin "*"s that are found in the body of a
+ * multiline doc-comment like this one.
+ */
+ function stripStars(text) {
+ return text.replace(/\s*\n\s*\*\s?/g, '\n').trim();
+ }
+
+ /**
+ * Parses the source of a test262 test case file into a JSON
+ * envelope record.
+ *
+ * <p>The input can be in old sputnik or ietestcenter style, or in
+ * the canonical test262 style. In all cases, we have an optional
+ * header, an optional "/*" comment possibly containing properties
+ * of the form<pre>
+ * @propName: propValue;
+ * </pre>which populate the test record. This is followed by the
+ * rest of the text, which is the test itself. In the case of an
+ * ietestcenter style test, this is followed by a call to
+ * <code>ES5Harness\.registerTest</code> to register a test record.
+ */
+ function parseTestEnvelope(src, name) {
+ var envelope = { testRecord: {} };
+ var envelopeMatch = testEnvelopePattern.exec(src);
+ if (!envelopeMatch) {
+ // Can't happen?
+ throw new Error('unrecognized: ' + name);
+ }
+ envelope.header = envelopeMatch[1].trim();
+
+ if (envelopeMatch[2]) {
+ var propTexts = envelopeMatch[2].split(/\s*\n\s*\*\s*@/);
+ envelope.comment = stripStars(propTexts.shift()), // notice side effect
+ propTexts.forEach(function(propText) {
+ var propName = propText.match(/^\w+/)[0];
+ var propVal = propText.substring(propName.length);
+ var propMatch = /^:?([^;]*);?\s*$/.exec(propVal);
+ if (propMatch) { propVal = propMatch[1]; }
+ propVal = stripStars(propVal);
+ if (propName in envelope.testRecord) {
+ throw new Error('duplicate: ' + propName);
+ }
+ envelope.testRecord[propName] = propVal;
+ });
+ }
+ envelope.rest = envelopeMatch[3]; // Do not trim
+
+ var registerMatch = registerPattern.exec(envelope.rest);
+ if (registerMatch) {
+ envelope.rest = registerMatch[1].trim();
+ envelope.registerExpr = registerMatch[2].trim();
+ } else if (envelope.rest.indexOf('ES5Harness.registerTest') >= 0) {
+ print(' \n--header---\n|' + envelope.header +
+ '|\n--rest-----\n|' + envelope.rest +
+ '|\n--harness--\n|' + envelope.registerExpr +
+ '|\n-----------\n');
+ throw new Error('Malformed harness? ' + name);
+ }
+ return envelope;
+ }
+
+ /**
+ * Given a function, return the source for an expression that, when
+ * evaluated in the environment the function assumes, will behave
+ * the same as calling that function in that environment.
+ */
+ function expressionize(func) {
+ var funcSrc = '' + func;
+ var cfbMatch = captureFuncBodyPattern.exec(funcSrc);
+ if (cfbMatch) {
+ // Look for special cases
+ var body = cfbMatch[1].trim();
+
+ var cebMatch = captureExprBodyPattern.exec(body);
+ if (cebMatch) { return '(' + cebMatch[1].trim() + ')'; }
+
+ var cpMatch = capturePredicatePattern.exec(body);
+ if (cpMatch) { return '(' + cpMatch[1].trim() + ')'; }
+
+ } else {
+ // signal an error?
+ }
+ return '(' + funcSrc + ').call(this)';
+ }
+
+ /**
+ * Given an ietestcenter style test, this <b>evaluates</b> the
+ * registration expression in order to gather the test record.
+ */
+ function gatherOne(envelope, name) {
+ if (envelope.testRecord) {
+ var propNames = Object.keys(envelope.testRecord);
+ if (propNames.length >= 1) {
+ // This need not be an error. It's just here so we notice the
+ // first time it happens. This would happen if an
+ // ietestcenter style test also had a comment with "@"
+ // property definitions.
+ throw new Error('unexpected in ' + name + ': ' + propNames);
+ }
+ }
+ var testRecords = [];
+
+ // Evaluating!!!!
+ platform.evalExprIn(envelope.registerExpr,
+ {
+ ES5Harness: {
+ registerTest: function(testRecord) {
+ testRecords.push(testRecord);
+ }
+ }
+ },
+ 'forceNonStrict');
+
+ if (testRecords.length !== 1) {
+ // We plan to lift this restriction in order to support test
+ // generators.
+ throw new Error('not singleton: ' + name);
+ }
+ var testRecord = testRecords[0];
+
+ if (typeof testRecord.test === 'function') {
+ testRecord.test = envelope.rest +
+ 'assertTrue(' + expressionize(testRecord.test) + ');\n';
+ }
+ if (typeof testRecord.precondition === 'function') {
+ var precondition = expressionize(testRecord.precondition);
+ if (precondition === '(true)') {
+ delete testRecord.precondition;
+ } else {
+ testRecord.precondition = precondition;
+ }
+ }
+
+ return testRecord;
+ };
+
+ /**
+ * Normalizes the properties of testRecord to be the canonical
+ * test262 style properties, that will be assumed by the new test
+ * runners.
+ */
+ function normalizeProps(testRecord) {
+ if (!testRecord.id && testRecord.name) {
+ testRecord.id = testRecord.name;
+ delete testRecord.name;
+ }
+
+ if (!('strict_only' in testRecord) && testRecord.strict === 1) {
+ testRecord.strict_only = '';
+ delete testRecord.strict;
+ }
+
+ if ('strict_mode_negative' in testRecord) {
+ if (!('strict_only' in testRecord)) {
+ testRecord.strict_only = '';
+ }
+ if (!'negative' in testRecord) {
+ testRecord.negative = testRecord.strict_mode_negative;
+ delete testRecord.strict_mode_negative;
+ }
+ }
+
+ if (!testRecord.negative && 'errortype' in testRecord) {
+ testRecord.negative = testRecord.errortype;
+ delete testRecord.errortype;
+ }
+
+ if (!testRecord.description && testRecord.assertion) {
+ testRecord.description = testRecord.assertion;
+ delete testRecord.assertion;
+ }
+ if (!testRecord.comment && testRecord.assertion) {
+ testRecord.comment = testRecord.assertion;
+ delete testRecord.assertion;
+ }
+ };
+ t262.normalizeProps = normalizeProps;
+
+ /**
+ * Parses the source of a test262 test case file into a normalized
+ * JSON test record.
+ */
+ function parseTestRecord(path, name) {
+ var nextPath = path.concat([name]);
+
+ var src = platform.read(nextPath);
+ var testRecord;
+ if (!src) { throw new Error('no src: ' + nextPath.join('/')); }
+ var envelope = parseTestEnvelope(src, name);
+
+ if (envelope.registerExpr) {
+ testRecord = gatherOne(envelope, name);
+ } else {
+ testRecord = envelope.testRecord;
+ if (!testRecord.test) {
+ testRecord.test = envelope.rest;
+ }
+ }
+ testRecord.header = envelope.header;
+ testRecord.comment = envelope.comment;
+
+ normalizeProps(testRecord);
+ return testRecord;
+ };
+ t262.parseTestRecord = parseTestRecord;
+
+ // The known ones will be rendered first, and in this order.
+ var KNOWN_PROPS = ['id', 'section', 'path', 'description',
+ 'strict_only', 'negative'];
+
+ /**
+ * Turns the (assumed) normalized test record into its string form
+ * in canonical test262 style.
+ *
+ * NOTE: This is currently destructive of testRecord. Easy to fix
+ *if it becomes a problem.
+ */
+ function formatTestRecord(testRecord) {
+ var test = testRecord.test;
+ delete testRecord.test;
+
+ function addProp(pname) {
+ if (pname in testRecord) {
+ result += ' * @' + pname;
+ if (testRecord[pname]) {
+ result += ': ' + testRecord[pname].replace(/\n/g, '\n * ');
+ }
+ result += ';\n';
+ delete testRecord[pname];
+ }
+ }
+
+ var result = testRecord.header + '\n\n';
+ delete testRecord.header;
+ result += '/**\n';
+ if (testRecord.comment) {
+ result += ' * ' + testRecord.comment.replace(/\n/g, '\n * ') + '\n *\n';
+ }
+ delete testRecord.comment;
+ KNOWN_PROPS.concat(['precondition']).forEach(addProp);
+ Object.keys(testRecord).forEach(addProp);
+ result += ' */\n\n' + test;
+ return result;
+ };
+ t262.formatTestRecord = formatTestRecord;
+
+ /**
+ * Reads the test case at pathStr and returns the source of that
+ * test case converted to canonical test262 style.
+ */
+ function convertTest(pathStr) {
+ var path = platform.toPath(pathStr);
+ var name = path.pop();
+ var testRecord = parseTestRecord(path, name);
+ var result = formatTestRecord(testRecord);
+ return result;
+ };
+ t262.convertTest = convertTest;
+
+ var SRC_DIRS = [
+ ['test', 'suite', 'other'],
+ ['test', 'suite', 'sputnik', 'Conformance'],
+ ['test', 'suite', 'ietestcenter']
+ ];
+
+ var CONV_DIR = ['test', 'suite', 'converted'];
+
+ var OUT_DIR = ['website', 'resources', 'scripts', 'testcases2'];
+
+ var ME_PATH = platform.CONVERTER_PATH.concat('convert.js');
+
+ /**
+ * Convert all the testcases found at inBase+relDir to test cases
+ * in canonical test262 style, to be stored at corresponding
+ * positions in outBase+relPath.
+ */
+ function convertAll(inBase, outBase, relPath) {
+ var inPath = inBase.concat(relPath);
+ var outPath = outBase.concat(relPath);
+ platform.mkdir(outPath);
+ platform.ls(inPath).forEach(function(name) {
+ var nextRelPath = relPath.concat([name]);
+ if (platform.isDirectory(inBase.concat(nextRelPath))) {
+ convertAll(inBase, outBase, nextRelPath);
+ } else if (/\.js$/.test(name)) {
+ var inFilePath = inPath.concat([name]);
+ var outFilePath = outPath.concat([name]);
+ platform.writeSpawn(
+ [ME_PATH],
+ 't262.convertTest("' + platform.toPathStr(inFilePath) + '")',
+ void 0,
+ outFilePath);
+ }
+ });
+ };
+ t262.convertAll = convertAll;
+
+ /**
+ * Do all the conversions (from sputnik style, ietestcenter style,
+ * or other to canonical test262 style) matching relPath.
+ */
+ function convert(opt_relPath) {
+ SRC_DIRS.forEach(function(srcDir) {
+ convertAll(srcDir, CONV_DIR, opt_relPath || []);
+ });
+ };
+ t262.convert = convert;
+
+ /**
+ * Reads all the test case records for the section corresponding to
+ * the directory at pathStr, and return a JSON record for a test
+ * case section, as would be uploaded to a browser-based test
+ * runner.
+ */
+ function buildSection(pathStr) {
+ var path = platform.toPath(pathStr);
+ if (!platform.isDirectory(path)) { throw new Error('not dir: ' + path); }
+
+ var jsFiles = platform.ls(path).filter(function(name) {
+ return /\.js$/.test(name);
+ });
+ var testRecords = jsFiles.map(function(name) {
+ var testRecord = parseTestRecord(path, name);
+
+ delete testRecord.header;
+ delete testRecord.comment;
+ return testRecord;
+ });
+ testRecords = testRecords.filter(function(testRecord) {
+ return testRecord !== null;
+ });
+ return {
+ testCollection: {
+ name: path[path.length -1],
+ numTests: testRecords.length,
+ tests: testRecords
+ }
+ };
+ };
+ t262.buildSection = buildSection;
+
+ /**
+ * Use the test cases at inBase+relPath to build the test
+ * collection portion of the website, at outBase.
+ */
+ function buildAll(inBase, outBase, relPath) {
+ var inPath = inBase.concat(relPath);
+ var hasJS = false;
+ platform.ls(inPath).forEach(function(name) {
+ var nextRelPath = relPath.concat([name]);
+ if (platform.isDirectory(inBase.concat(nextRelPath))) {
+ buildAll(inBase, outBase, nextRelPath);
+ } else if (/\.js$/.test(name)) {
+ hasJS = true;
+ }
+ });
+ if (hasJS) {
+ var name = relPath[relPath.length -1] + '.json';
+ var outFilePath = outBase.concat([name]);
+ platform.writeSpawn(
+ [ME_PATH],
+ 't262.asJSONTxt(t262.buildSection("' +
+ platform.toPathStr(inPath) + '"))',
+ void 0,
+ outFilePath);
+ }
+ };
+ t262.buildAll = buildAll;
+
+ /**
+ * Build those test case files for the website corresponding to the
+ * test cases matching relPath.
+ *
+ * <p>Right now it's building from the pre-converted test
+ * files. Once we switch over to converted as the maintained
+ * sources, we should change this.
+ */
+ function buildWebSite(opt_relPath) {
+ SRC_DIRS.forEach(function(srcDir) {
+ buildAll(srcDir, OUT_DIR, opt_relPath || []);
+ });
+// buildAll(CONV_DIR, OUT_DIR, opt_relPath || []);
+ };
+ t262.buildWebSite = buildWebSite;
+
+ })(this);
diff --git a/tools/converter/v8PosixPlatform.js b/tools/converter/v8PosixPlatform.js
new file mode 100644
index 000000000..cfd849b07
--- /dev/null
+++ b/tools/converter/v8PosixPlatform.js
@@ -0,0 +1,275 @@
+
+
+/**
+ * Each implementation of *Platform.js abstracts the underlying OS and JS
+ * engine peculiarities.
+ *
+ * <p>The implementation here is specific to the v8 shell running on a
+ * Posix platform.
+ */
+(function (global) {
+ "use strict";
+
+ /////////////////// Development Switches /////////////////
+
+ var VERBOSE = true;
+
+ // Affects side effecting os operations,
+ // currently only platform.writeSpawn and platform.mkdir.
+ var DRY_RUN = false;
+
+ // When converting paths to path strings, should the pathstring be
+ // relative to the TEST262_ROOT, or should it be relative to the
+ // current working directory?
+ var ABSOLUTE_PATHSTR = false;
+
+ ////////////////////////////////////////////////////////
+
+ global.t262 = global.t262 || {};
+
+ var platform = global.t262.platform = {};
+
+ /**
+ * Appends a bunch of RegExps together into a single RegExp,
+ * solving both the RegExp-one-liner problem and the doubled
+ * backslash problem when composing literal string.
+ *
+ * <p>The arguments can be any mixture of RegExps and strings. The
+ * strings are added as is without escaping -- BEWARE. If
+ * arguments[0] is a RegExp, we use its flag on the resuting RegExp.
+ *
+ * <p>Not platform dependent, so does not really belong in this
+ * file.
+ */
+ function regExp(var_args) {
+ var args = [].slice.call(arguments, 0);
+ var reSrc = args.map(function(arg) {
+ return (typeof arg === 'string') ? arg : arg.source;
+ }).join('');
+ var flags = '';
+ if (typeof args[0] === 'object') {
+ var parts = (''+args[0]).split('/');
+ flags = parts[parts.length -1];
+ }
+ return new RegExp(reSrc, flags);
+ }
+ platform.regExp = regExp;
+
+ ////////////////// Needed for building and running //////////////
+
+ try {
+ read('tools/converter/v8PosixPlatform.js');
+ } catch (err) {
+ throw new Error('Must run in a test262 source root');
+ }
+
+ var ABS_ROOT = os.system('pwd', ['-P']).trim().split('/');
+
+ var TEST262_ROOT = ABSOLUTE_PATHSTR ? ABS_ROOT : [];
+
+ var TEST262_ROOT_STR = TEST262_ROOT.join('/');
+
+ var CONVERTER_PATH = ['tools', 'converter'];
+ platform.CONVERTER_PATH = CONVERTER_PATH;
+
+ var ME_PATH = CONVERTER_PATH.concat('v8PosixPlatform.js');
+
+ /**
+ *
+ */
+ function validatePath(path) {
+ var pathStr = path.join('/');
+ path.forEach(function(segment) {
+ if (segment === '') {
+ throw new Error('A path cannot have empty segment: ' + pathStr);
+ }
+ if (segment === '/') {
+ throw new Error('Path insufficiently parsed: ' + pathStr);
+ }
+ if (segment === '..') {
+ throw new Error('Cannot use "..": ' + pathStr);
+ }
+ });
+ return path;
+ }
+
+ /**
+ * Converts a path to a pathStr.
+ *
+ * A path is an array of filenames relative to TEST262_ROOT. A
+ * pathStr is a (possibly fully qualified string) for referring to
+ * that string on the current platform, according to the operations
+ * in this *Platform.js file.
+ */
+ function toPathStr(path) {
+ validatePath(path);
+ return TEST262_ROOT.concat(path).join('/');
+ };
+ platform.toPathStr = toPathStr;
+
+ /**
+ * Returns the text found at path, with newlines normalized and
+ * any initial BOM (Unicode Byte Order Mark) removed.
+ */
+ platform.read = function(path) {
+ var text = read(toPathStr(path)).
+ replace(/\r\n/g, '\n').
+ replace(/\r/g, '\n');
+ if (text.charCodeAt(0) === 0xfeff) { return text.substring(1); }
+ return text;
+ };
+
+ /**
+ * How one JavaScript script possibly spawns another and possibly
+ * redirects its printed form to a chosen file (or resource).
+ *
+ * <p>For example, if !DRY_RUN, then<pre>
+ * writeSpawn([], '+arguments[0] + +arguments[1]', ['3', '5'])
+ * </pre>
+ * should return the string "8", whether or not writeSpawn decides
+ * to spawn.
+ *
+ * @param scriptPaths An array of path arrays of JavaScript source
+ * files to be loaded into the spawned JS engine (in addition to
+ * the spawning platform file) if we are indeed spawning.
+ * @param opt_exprSrc An expression to be evaluated in an
+ * environment in which "arguments" is bound to the list of strings
+ * provided by opt_args. The result is the value of the expression
+ * coerced to a string, unfortunately, as prepended by whatever
+ * these scripts (if spawned) have already written to their
+ * stdout. On platforms (like SES) where this can be a safely
+ * confining evaluation, it should be. The implementation here is
+ * not safe.
+ * @param opt_args A list of strings to be bound to 'arguments'
+ * both in opt_expr and in the possibly spawed scripts.
+ * @param opt_targetPath A path array naming a file where the
+ * result of opt_exprSrc should be written. On v8 currently, if
+ * this is provided, then writeSpawn will spawn, since we have no
+ * other way to implement this functionality. In the browser
+ * context, the result is PUT (using XHR) to the target resource.
+ * @param opt_spawn_required If truthy, forces spawning.
+ * @returns If there is a target, then the null string. Otherwise,
+ * the string result of evaluating opt_exprSrc.
+ */
+ platform.writeSpawn = function(scriptPaths,
+ opt_exprSrc,
+ opt_args,
+ opt_targetPath,
+ opt_spawn_required,
+ opt_forceNonStrict) {
+ if (opt_exprSrc && !opt_targetPath && !opt_spawn_required) {
+ var str = '(function(/*var_args*/) {';
+ if (opt_forceNonStrict !== 'forceNonStrict') {
+ str += '"use strict";';
+ }
+ str += ' return (' + opt_exprSrc + '); })';
+ return ''+(1,eval)(str).apply(void 0, opt_args || []);
+ }
+
+ var cmd = 'v8 ' + toPathStr(ME_PATH) + ' ';
+ cmd += scriptPaths.map(toPathStr).join(' ');
+
+ if (opt_exprSrc) {
+ cmd += ' -e ' + JSON.stringify('print(' + opt_exprSrc + ')');
+ }
+ if (opt_args) {
+ cmd += ' -- ' + opt_args.map(JSON.stringify).join(' ');
+ }
+ if (opt_targetPath) {
+ cmd += ' > ' + toPathStr(opt_targetPath);
+ }
+ if (VERBOSE || DRY_RUN) { print(cmd); }
+ if (DRY_RUN) { return ''; }
+ return os.system('bash', ['-c', cmd]);
+ };
+
+ ////////////////// Only needed for building /////////////////////
+
+ /**
+ * Calls a non-strict indirect eval function on exprSrc.
+ *
+ * On platforms (like SES) where this can be a safely confining
+ * evaluation, it should be. The implementation here is not safe.
+ */
+ platform.evalExprIn = function(exprSrc, env, opt_forceNonStrict) {
+ var varNames = Object.getOwnPropertyNames(env);
+ var str = '(function(' + varNames.join(',') + ') {';
+ if (opt_forceNonStrict !== 'forceNonStrict') {
+ str += '"use strict";';
+ }
+ str += ' return (' + exprSrc + '); })';
+ return (1,eval)(str).apply(void 0, varNames.map(function(varName) {
+ return env[varName];
+ }));
+ };
+
+ /**
+ * Converts a pathStr to a path.
+ *
+ * See toPathStr.
+ */
+ function toPath(pathStr) {
+ if (pathStr[0] === '/') {
+ if (pathStr.indexOf(TEST262_ROOT_STR + '/') !== 0) {
+ throw new Error('"' + pathStr + '" must start with "' +
+ TEST262_ROOT_STR + '/"');
+ }
+ pathStr = pathStr.substring(TEST262_ROOT_STR.length + 1);
+ }
+ return validatePath(pathStr.split('/'));
+ }
+ platform.toPath = toPath;
+
+ /**
+ * Does path name a directory?
+ */
+ platform.isDirectory = function(path) {
+ var fileOut = os.system('file', [toPathStr(path)]);
+ var fileMatch = fileOut.match(/:\s*([^:]*)\s*$/);
+ if (!fileMatch) { return null; }
+ var fileType = fileMatch[1].trim();
+ return fileType === 'directory';
+ };
+
+ /**
+ * A list of the filenames found in path, which must name a
+ * directory.
+ */
+ platform.ls = function(path) {
+ var pathStr = toPathStr(path);
+ var lines = os.system('ls', [pathStr]).trim();
+ if (lines === '') { return []; }
+ return lines.split('\n');
+ };
+
+ /**
+ * Emits the jsonRecord serialized as JSON, either compactly or
+ * readably according to VERBOSE.
+ */
+ function asJSONTxt(jsonRecord) {
+ if (VERBOSE) {
+ return JSON.stringify(jsonRecord, void 0, ' ');
+ } else {
+ return JSON.stringify(jsonRecord);
+ }
+ }
+ global.t262.asJSONTxt = platform.asJSONTxt = asJSONTxt;
+
+ platform.mkdir = function(path) {
+ var pathStr = toPathStr(path);
+ if (DRY_RUN) {
+ print('mkdir ' + pathStr);
+ return;
+ }
+ try {
+ os.mkdirp(pathStr);
+ } catch (err) {
+ print('***could not mkdir: ' + pathStr);
+ throw err;
+ }
+ };
+
+ ////////////////// Only needed for running //////////////////////
+
+
+ })(this);