diff --git a/jstests/core/distinct4.js b/jstests/core/distinct4.js index 74f89cde4e0..5e4a1fd85a2 100644 --- a/jstests/core/distinct4.js +++ b/jstests/core/distinct4.js @@ -19,7 +19,7 @@ // from command interface assert.commandFailedWithCode(t.runCommand("distinct", {"key": {a: 1}}), - 14); // ErrorCodes::TypeMismatch + ErrorCodes.TypeMismatch); //second argument should be a document or error @@ -29,7 +29,7 @@ // from command interface assert.commandFailedWithCode(t.runCommand("distinct", {"key": "a", "query": "a"}), - 14); // ErrorCodes::TypeMismatch + ErrorCodes.TypeMismatch); diff --git a/src/mongo/base/SConscript b/src/mongo/base/SConscript index c1c4480ccb0..700e0b14cd7 100644 --- a/src/mongo/base/SConscript +++ b/src/mongo/base/SConscript @@ -5,7 +5,7 @@ Import("env") generateErrorCodes = env.Command( target=['error_codes.h', 'error_codes.cpp'], source=['generate_error_codes.py', 'error_codes.err'], - action='$PYTHON $SOURCES $TARGETS') + action=['$PYTHON ${SOURCES[0]} cpp ${SOURCES[1]} --cpp-header=${TARGETS[0]} --cpp-source=${TARGETS[1]}']) env.Alias('generated-sources', generateErrorCodes) env.CppUnitTest('base_test', diff --git a/src/mongo/base/generate_error_codes.py b/src/mongo/base/generate_error_codes.py index c68a5f13086..6fa0300afbc 100644 --- a/src/mongo/base/generate_error_codes.py +++ b/src/mongo/base/generate_error_codes.py @@ -36,19 +36,29 @@ error_code("symbol2", code2) error_class("class1", ["symbol1", "symbol2, ..."]) Usage: - python generate_error_codes.py
+ python generate_error_codes.py [options] """ +usage_msg = "usage: %prog /path/to/error_codes.err [options]" + +from optparse import OptionParser import sys - def main(argv): - if len(argv) != 4: - usage("Wrong number of arguments.") - - error_codes, error_classes = parse_error_definitions_from_file(argv[1]) + generator = argv[1] + error_codes, error_classes = parse_error_definitions_from_file(argv[2]) check_for_conflicts(error_codes, error_classes) - generate_header(argv[2], error_codes, error_classes) - generate_source(argv[3], error_codes, error_classes) + if (generator == 'cpp'): + if (len(argv) != 5): + usage('Wrong number of arguments') + cpp_gen = cpp_generator(error_codes, error_classes) + cpp_gen.generate() + elif (generator == 'js'): + if (len(argv) != 4): + usage('Wrong number of arguments') + js_gen = js_generator(error_codes, error_classes) + js_gen.generate() + else: + usage('Must specify which generator(s) to use.') def die(message=None): sys.stderr.write(message or "Fatal error\n") @@ -120,37 +130,147 @@ def has_missing_error_codes(error_codes, error_classes): failed = True return failed -def generate_header(filename, error_codes, error_classes): +class base_generator(object): + def __init__(self, error_codes, error_classes): + self.error_codes = error_codes + self.error_classes = error_classes - enum_declarations = ',\n '.join('%s = %s' % ec for ec in error_codes) - predicate_declarations = ';\n '.join( - 'static bool is%s(Error err)' % ec[0] for ec in error_classes) + def parseOptions(self, options, usage_msg): + parser = OptionParser(usage=usage_msg) + for (f,d,n,m,h) in options: + parser.add_option(f,dest=d,nargs=n,metavar=m,help=h) + (options, args) = parser.parse_args() + return options - open(filename, 'wb').write(header_template % dict( - error_code_enum_declarations=enum_declarations, - error_code_class_predicate_declarations=predicate_declarations)) -def generate_source(filename, error_codes, error_classes): - symbol_to_string_cases = ';\n '.join( - 'case %s: return "%s"' % (ec[0], ec[0]) for ec in error_codes) - string_to_symbol_cases = ';\n '.join( - 'if (name == "%s") return %s' % (ec[0], ec[0]) - for ec in error_codes) - int_to_symbol_cases = ';\n '.join( - 'case %s: return %s' % (ec[0], ec[0]) for ec in error_codes) - predicate_definitions = '\n '.join( - generate_error_class_predicate_definition(*ec) for ec in error_classes) - open(filename, 'wb').write(source_template % dict( - symbol_to_string_cases=symbol_to_string_cases, - string_to_symbol_cases=string_to_symbol_cases, - int_to_symbol_cases=int_to_symbol_cases, - error_code_class_predicate_definitions=predicate_definitions)) +class js_generator(base_generator): + def __init__(self, error_codes, error_classes): + super(js_generator, self).__init__(error_codes, error_classes) + options = [('--js-source','js_source',1,'DEST_JS_SOURCE','specify dest JS source file to save to')] + options = self.parseOptions(options, usage_msg) + if (options.js_source): + self.js_source = options.js_source + else: + usage('Must specify JS source files') -def generate_error_class_predicate_definition(class_name, code_names): - cases = '\n '.join('case %s:' % c for c in code_names) - return error_class_predicate_template % dict(class_name=class_name, cases=cases) + def generate(self): + self.generate_source() -header_template = '''// AUTO-GENERATED FILE DO NOT EDIT + def generate_source(self): + string_to_int_cases = ',\n '.join( + '%s: %s' % (ec[0], ec[1]) for ec in self.error_codes) + int_to_string_cases = ',\n '.join( + '%s: \'%s\'' % (ec[1], ec[0]) for ec in self.error_codes) + predicate_definitions = '\n\n'.join( + self.generate_error_class_predicate_definition(*ec) for ec in self.error_classes) + open(self.js_source, 'wb').write(self.source_template % dict( + string_to_int_cases=string_to_int_cases, + int_to_string_cases=int_to_string_cases + )) + + def generate_error_class_predicate_definition(self, class_name, code_names): + cases = '\n '.join('case \'%s\':' % c for c in code_names) + return self.error_class_predicate_template % dict(class_name=class_name, cases=cases) + + source_template = '''// AUTO-GENERATED FILE DO NOT EDIT +// See src/mongo/base/generate_error_codes.py +/* Copyright 2015 MongoDB, Inc. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License, version 3, +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +* +* As a special exception, the copyright holders give permission to link the +* code of portions of this program with the OpenSSL library under certain +* conditions as described in each individual source file and distribute +* linked combinations including the program with the OpenSSL library. You +* must comply with the GNU Affero General Public License in all respects +* for all of the code used other than as permitted herein. If you modify +* file(s) with this exception, you may extend this exception to your +* version of the file(s), but you are not obligated to do so. If you do not +* wish to do so, delete this exception statement from your version. If you +* delete this exception statement from all source files in the program, +* then also delete it in the license file. +*/ + +var ErrorCodes = { + %(string_to_int_cases)s +}; + +var ErrorCodeStrings = { + %(int_to_string_cases)s +}; +''' + + error_class_predicate_template = '''function is%(class_name)s(err) { + if (typeof err === 'string') { + error = err; + } else if (typeof err === 'number') { + error = ErrorCodeStrings[err]; + } + switch (error) { + %(cases)s + return true; + default: + return false; + } +} +''' + +class cpp_generator(base_generator): + def __init__(self, error_codes, error_classes): + super(cpp_generator, self).__init__(error_codes, error_classes) + options = [('--cpp-header','cpp_header',1,'DEST_CPP_HEADER','specify dest CPP header file to save to'), ('--cpp-source','cpp_source',1,'DEST_CPP_SOURCE','specify dest CPP source file to save to')] + options = self.parseOptions(options, usage_msg) + if (options.cpp_header and options.cpp_source): + self.cpp_header = options.cpp_header + self.cpp_source = options.cpp_source + else: + usage('Must specify CPP header and source files') + + def generate(self): + self.generate_header() + self.generate_source() + + def generate_header(self): + + enum_declarations = ',\n '.join('%s = %s' % ec for ec in self.error_codes) + predicate_declarations = ';\n '.join( + 'static bool is%s(Error err)' % ec[0] for ec in self.error_classes) + + open(self.cpp_header, 'wb').write(self.header_template % dict( + error_code_enum_declarations=enum_declarations, + error_code_class_predicate_declarations=predicate_declarations)) + + def generate_source(self): + symbol_to_string_cases = ';\n '.join( + 'case %s: return "%s"' % (ec[0], ec[0]) for ec in self.error_codes) + string_to_symbol_cases = ';\n '.join( + 'if (name == "%s") return %s' % (ec[0], ec[0]) + for ec in self.error_codes) + int_to_symbol_cases = ';\n '.join( + 'case %s: return %s' % (ec[0], ec[0]) for ec in self.error_codes) + predicate_definitions = '\n '.join( + self.generate_error_class_predicate_definition(*ec) for ec in self.error_classes) + open(self.cpp_source, 'wb').write(self.source_template % dict( + symbol_to_string_cases=symbol_to_string_cases, + string_to_symbol_cases=string_to_symbol_cases, + int_to_symbol_cases=int_to_symbol_cases, + error_code_class_predicate_definitions=predicate_definitions)) + + def generate_error_class_predicate_definition(self, class_name, code_names): + cases = '\n '.join('case %s:' % c for c in code_names) + return self.error_class_predicate_template % dict(class_name=class_name, cases=cases) + + header_template = '''// AUTO-GENERATED FILE DO NOT EDIT // See src/mongo/base/generate_error_codes.py /* Copyright 2014 MongoDB, Inc. * @@ -178,52 +298,41 @@ header_template = '''// AUTO-GENERATED FILE DO NOT EDIT * delete this exception statement from all source files in the program, * then also delete it in the license file. */ - #pragma once - #include - #include "mongo/base/string_data.h" - namespace mongo { - /** * This is a generated class containing a table of error codes and their corresponding error * strings. The class is derived from the definitions in src/mongo/base/error_codes.err file. * * Do not update this file directly. Update src/mongo/base/error_codes.err instead. */ - class ErrorCodes { public: enum Error { %(error_code_enum_declarations)s, MaxError }; - static std::string errorString(Error err); - /** * Parses an Error from its "name". Returns UnknownError if "name" is unrecognized. * * NOTE: Also returns UnknownError for the string "UnknownError". */ static Error fromString(StringData name); - /** * Casts an integer "code" to an Error. Unrecognized codes are preserved, meaning * that the result of a call to fromInt() may not be one of the values in the * Error enumeration. */ static Error fromInt(int code); - %(error_code_class_predicate_declarations)s; }; - } // namespace mongo ''' -source_template = '''// AUTO-GENERATED FILE DO NOT EDIT + source_template = '''// AUTO-GENERATED FILE DO NOT EDIT // See src/mongo/base/generate_error_codes.py /* Copyright 2014 MongoDB, Inc. * @@ -251,39 +360,30 @@ source_template = '''// AUTO-GENERATED FILE DO NOT EDIT * delete this exception statement from all source files in the program, * then also delete it in the license file. */ - #include "mongo/base/error_codes.h" - - #include "mongo/util/mongoutils/str.h" - namespace mongo { - std::string ErrorCodes::errorString(Error err) { switch (err) { %(symbol_to_string_cases)s; default: return mongoutils::str::stream() << "Location" << err; } } - ErrorCodes::Error ErrorCodes::fromString(StringData name) { %(string_to_symbol_cases)s; return UnknownError; } - ErrorCodes::Error ErrorCodes::fromInt(int code) { return static_cast(code); } - %(error_code_class_predicate_definitions)s - namespace { static_assert(sizeof(ErrorCodes::Error) == sizeof(int), "sizeof(ErrorCodes::Error) == sizeof(int)"); } // namespace } // namespace mongo ''' -error_class_predicate_template = '''bool ErrorCodes::is%(class_name)s(Error err) { + error_class_predicate_template = '''bool ErrorCodes::is%(class_name)s(Error err) { switch (err) { %(cases)s return true; @@ -292,5 +392,7 @@ error_class_predicate_template = '''bool ErrorCodes::is%(class_name)s(Error err) } } ''' + + if __name__ == '__main__': main(sys.argv) diff --git a/src/mongo/scripting/engine.cpp b/src/mongo/scripting/engine.cpp index 56288b24cef..b00ff423098 100644 --- a/src/mongo/scripting/engine.cpp +++ b/src/mongo/scripting/engine.cpp @@ -286,6 +286,7 @@ extern const JSFile utils; extern const JSFile utils_sh; extern const JSFile utils_auth; extern const JSFile bulk_api; +extern const JSFile error_codes; } void Scope::execCoreFiles() { @@ -297,6 +298,7 @@ void Scope::execCoreFiles() { execSetup(JSFiles::mr); execSetup(JSFiles::query); execSetup(JSFiles::bulk_api); + execSetup(JSFiles::error_codes); execSetup(JSFiles::collection); execSetup(JSFiles::crud_api); execSetup(JSFiles::explain_query); diff --git a/src/mongo/shell/SConscript b/src/mongo/shell/SConscript index 0541400a9e7..547848ad3a3 100644 --- a/src/mongo/shell/SConscript +++ b/src/mongo/shell/SConscript @@ -2,8 +2,14 @@ Import("env") +generateJSErrorCodes = env.Command( + target=['error_codes.js'], + source=['$BUILD_DIR/mongo/base/generate_error_codes.py', '$BUILD_DIR/mongo/base/error_codes.err'], + action=['$PYTHON ${SOURCES[0]} js ${SOURCES[1]} --js-source=${TARGETS[0]}']) +env.Alias('generated-sources', generateJSErrorCodes) + # Files added here need to be added in scripting/engine.cpp and buildscripts/vcxproj.header as well. -env.JSHeader( +js_header = env.JSHeader( target="mongo.cpp", source=[ "assert.js", @@ -13,6 +19,7 @@ env.JSHeader( "db.js", "explain_query.js", "explainable.js", + "error_codes.js", "mongo.js", "mr.js", "query.js", @@ -21,8 +28,9 @@ env.JSHeader( "utils.js", "utils_sh.js", "utils_auth.js", - ], + ] ) +env.Depends(js_header, env.Alias('generated-sources')) # Files added here need to be added in shell/shell_utils.cpp and buildscripts/vcxproj.header as # well.