Commit b786dfa7 authored by Stephen Morris's avatar Stephen Morris
Browse files

[trac558] Make message IDs external symbols

The .h file now contains external references to message IDs and
the .cc file contains both the definition and the initialization
of the global dictionary with the associated text.
parent 10b0c31c
......@@ -20,7 +20,7 @@ liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
liblog_la_SOURCES += message_exception.h message_exception.cc
liblog_la_SOURCES += message_initializer.cc message_initializer.h
liblog_la_SOURCES += message_reader.cc message_reader.h
liblog_la_SOURCES += message_types.h message_types.cc
liblog_la_SOURCES += message_types.h
liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
liblog_la_SOURCES += strutil.h strutil.cc
# liblog_la_SOURCES += xdebuglevel.cc xdebuglevel.h
......
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -46,7 +46,7 @@ static const char* VERSION = "1.0-0";
///
/// \li A .h file containing message definition
/// \li A .cc file containing code that adds the messages to the program's
/// message disctionary at start-up time.
/// message dictionary at start-up time.
///
/// Alternatively, the program can produce a .py file that contains the
/// message definitions.
......@@ -80,12 +80,9 @@ version() {
void
usage() {
cout <<
"Usage: message [-h] [-p] [-v] <message-file>\n" <<
"Usage: message [-h] [-v] <message-file>\n" <<
"\n" <<
"-h Print this message and exit\n" <<
"-p Output a Python module holding the message definitions.\n" <<
" By default a C++ header file and implementation file are\n" <<
" written.\n" <<
"-v Print the program version and exit\n" <<
"\n" <<
"<message-file> is the name of the input message file.\n";
......@@ -108,12 +105,10 @@ currentTime() {
// Convert to string and strip out the trailing newline
string current_time = buffer;
return (isc::strutil::trim(current_time));
return isc::strutil::trim(current_time);
}
/// \brief Create Header Sentinel
///
/// Given the name of a file, create an #ifdef sentinel name. The name is
......@@ -131,7 +126,7 @@ sentinel(Filename& file) {
string ext = file.extension();
string sentinel_text = "__" + name + "_" + ext.substr(1);
isc::strutil::uppercase(sentinel_text);
return (sentinel_text);
return sentinel_text;
}
......@@ -158,7 +153,7 @@ quoteString(const string& instring) {
outstring += instring[i];
}
return (outstring);
return outstring;
}
......@@ -172,16 +167,16 @@ quoteString(const string& instring) {
/// \return Sorted list of message IDs
vector<string>
sortedIdentifiers(MessageDictionary* dictionary) {
sortedIdentifiers(MessageDictionary& dictionary) {
vector<string> ident;
for (MessageDictionary::const_iterator i = dictionary->begin();
i != dictionary->end(); ++i) {
for (MessageDictionary::const_iterator i = dictionary.begin();
i != dictionary.end(); ++i) {
ident.push_back(i->first);
}
sort(ident.begin(), ident.end());
return (ident);
return ident;
}
......@@ -202,28 +197,16 @@ sortedIdentifiers(MessageDictionary* dictionary) {
vector<string>
splitNamespace(string ns) {
vector<string> components;
if (ns == "::") {
// Unnamed namespace
components.push_back("");
} else {
// Namespaces components are separated by double colon characters -
// convert to single colons.
size_t dcolon;
while ((dcolon = ns.find("::")) != string::npos) {
ns.replace(dcolon, 2, ":");
}
// ... and return the vector of namespace components split on the single
// colon.
components = isc::strutil::tokens(ns, ":");
// Namespaces components are separated by double colon characters -
// convert to single colons.
size_t dcolon;
while ((dcolon = ns.find("::")) != string::npos) {
ns.replace(dcolon, 2, ":");
}
return (components);
// ... and return the vector of namespace components split on the single
// colon.
return isc::strutil::tokens(ns, ":");
}
......@@ -231,18 +214,12 @@ splitNamespace(string ns) {
///
/// Writes the lines listing the namespaces in use.
void
writeOpeningNamespace(ostream& output, vector<string>& ns) {
writeOpeningNamespace(ostream& output, const vector<string>& ns) {
if (!ns.empty()) {
if (ns[0].empty()) {
// Empty namespace
output << "namespace {\n";
} else {
// Output namespaces in correct order
for (int i = 0; i < ns.size(); ++i) {
output << "namespace " << ns[i] << " {\n";
}
// Output namespaces in correct order
for (int i = 0; i < ns.size(); ++i) {
output << "namespace " << ns[i] << " {\n";
}
output << "\n";
}
......@@ -253,14 +230,10 @@ writeOpeningNamespace(ostream& output, vector<string>& ns) {
///
/// Writes the lines listing the namespaces in use.
void
writeClosingNamespace(ostream& output, vector<string>& ns) {
writeClosingNamespace(ostream& output, const vector<string>& ns) {
if (!ns.empty()) {
if (ns[0].empty()) {
output << "} // Unnamed namespace\n";
} else {
for (int i = ns.size() - 1; i >= 0; --i) {
output << "} // namespace " << ns[i] << "\n";
}
for (int i = ns.size() - 1; i >= 0; --i) {
output << "} // namespace " << ns[i] << "\n";
}
output << "\n";
}
......@@ -279,8 +252,8 @@ writeClosingNamespace(ostream& output, vector<string>& ns) {
/// \param dictionary Dictionary holding the message definitions.
void
writeHeaderFile(const string& file, const string& prefix, const string& ns,
string& mi_name, MessageDictionary* dictionary)
writeHeaderFile(const string& file, const string& prefix,
const vector<string>& ns_components, MessageDictionary& dictionary)
{
Filename message_file(file);
Filename header_file(message_file.useAsDefault(".h"));
......@@ -308,52 +281,20 @@ writeHeaderFile(const string& file, const string& prefix, const string& ns,
"#define " << sentinel_text << "\n" <<
"\n" <<
"#include <log/message_types.h>\n" <<
"#include <log/message_initializer.h>\n" <<
"\n";
// Namespaces
vector<string> ns_components = splitNamespace(ns);
// Write the message identifiers, bounded by a namespace declaration
writeOpeningNamespace(hfile, ns_components);
// Now the m,essage identifications themselves.
vector<string> idents = sortedIdentifiers(dictionary);
for (vector<string>::const_iterator j = idents.begin();
j != idents.end(); ++j) {
hfile << "static const isc::log::MessageID " << prefix << *j <<
" = \"" << *j << "\";\n";
hfile << "extern const isc::log::MessageID " << prefix << *j << ";\n";
}
hfile << "\n";
// Close off namespaces if appropriate.
writeClosingNamespace(hfile, ns_components);
// Now create the reference to the message initializer to ensure that
// it gets run at program startup. Note that even the instantiator
// object is given its own unique name - multiple message header files
// might be included in the file, and identical multiple static names
// would clash.
hfile <<
"namespace isc {\n" <<
"namespace log {\n" <<
"\n" <<
"// The next two objects are needed to bring the default message\n" <<
"// definitions into the program. They make sure that the file\n" <<
"// containing the message text is included in the link process.\n" <<
"//\n" <<
"// The objects are uniquely named (with file name and date and\n" <<
"// time of compilation) to avoid clashes with other objects of\n" <<
"// the same type, either by another #include or as a global\n" <<
"// symbol in another module.\n" <<
"\n" <<
"extern MessageInitializer " << mi_name << ";\n" <<
"static MessageInstantiator instantiate_" << mi_name << "(\n" <<
" &" << mi_name << ");\n" <<
"\n" <<
"} // namespace log\n" <<
"} // namespace isc\n";
// ... and finally the postamble
hfile << "#endif // " << sentinel_text << "\n";
......@@ -384,12 +325,13 @@ replaceNonAlphaNum(char c) {
/// \brief Write Program File
///
/// Writes the C++ source code file. This defines an external objects whose
/// constructor is run at initialization time. The constructor adds the message
/// definitions to the main global dictionary.
/// Writes the C++ source code file. This defines the text of the message
/// symbols, as well as the initializer object that sets the entries in
/// the global dictionary.
string
writeProgramFile(const string& file, MessageDictionary* dictionary)
void
writeProgramFile(const string& file, const string& prefix,
const vector<string>& ns_components, MessageDictionary& dictionary)
{
Filename message_file(file);
Filename program_file(message_file.useAsDefault(".cc"));
......@@ -410,47 +352,51 @@ writeProgramFile(const string& file, MessageDictionary* dictionary)
currentTime() << "\n" <<
"\n" <<
"#include <cstddef>\n" <<
"#include <log/message_types.h>\n" <<
"#include <log/message_initializer.h>\n" <<
"\n" <<
"\n";
// Declare the message symbols themselves.
writeOpeningNamespace(ccfile, ns_components);
vector<string> idents = sortedIdentifiers(dictionary);
for (vector<string>::const_iterator j = idents.begin();
j != idents.end(); ++j) {
ccfile << "extern const isc::log::MessageID " << prefix << *j <<
" = \"" << *j << "\";\n";
}
ccfile << "\n";
writeClosingNamespace(ccfile, ns_components);
// Now the code for the message initialization.
ccfile <<
"namespace {\n" <<
"\n" <<
"const char* values[] = {\n";
// Output the identifiers and the associated text.
vector<string> idents = sortedIdentifiers(dictionary);
idents = sortedIdentifiers(dictionary);
for (vector<string>::const_iterator i = idents.begin();
i != idents.end(); ++i) {
ccfile << " \"" << *i << "\", \"" <<
quoteString(dictionary->getText(*i)) << "\",\n";
quoteString(dictionary.getText(*i)) << "\",\n";
}
// ... and the postamble
ccfile <<
" NULL\n" <<
"};\n" <<
"\n" <<
"} // Anonymous namespace\n" <<
"\n";
// Now construct a unique name. We don't put the message initializer as
// a static variable or in an anonymous namespace lest the C++
// compiler's optimizer decides it can optimise it away.
string unique_name = program_file.name() + program_file.extension() +
"_" + currentTime();
transform(unique_name.begin(), unique_name.end(), unique_name.begin(),
replaceNonAlphaNum);
// ... and write the initialization code
ccfile <<
"namespace isc {\n" <<
"namespace log {\n" <<
"isc::log::MessageInitializer initializer(values);\n" <<
"\n" <<
"MessageInitializer " << unique_name << "(values);\n" <<
"\n" <<
"} // namespace log\n" <<
"} // namespace isc\n" <<
"} // Anonymous namespace\n" <<
"\n";
// Report errors (if any) and exit
if (ccfile.fail()) {
throw MessageException(MSG_WRITERR, program_file.fullName(),
......@@ -458,8 +404,6 @@ writeProgramFile(const string& file, MessageDictionary* dictionary)
}
ccfile.close();
return (unique_name);
}
catch (MessageException&) {
ccfile.close();
......@@ -518,15 +462,15 @@ main(int argc, char** argv) {
switch (opt) {
case 'h':
usage();
return (0);
return 0;
case 'v':
version();
return (0);
return 0;
default:
// A message will have already been output about the error.
return (1);
return 1;
}
}
......@@ -534,11 +478,11 @@ main(int argc, char** argv) {
if (optind < (argc - 1)) {
cout << "Error: excess arguments in command line\n";
usage();
return (1);
return 1;
} else if (optind >= argc) {
cout << "Error: missing message file\n";
usage();
return (1);
return 1;
}
string message_file = argv[optind];
......@@ -551,13 +495,18 @@ main(int argc, char** argv) {
MessageReader reader(&dictionary);
reader.readFile(message_file);
// Write the file that defines the message text
std::string mi_name =
writeProgramFile(message_file, &dictionary);
// Get the namespace into which the message definitions will be put and
// split it into components.
vector<string> ns_components = splitNamespace(reader.getNamespace());
// Write the header file.
writeHeaderFile(message_file, reader.getPrefix(), ns_components,
dictionary);
// Write the file that defines the message symbols and text
writeProgramFile(message_file, reader.getPrefix(), ns_components,
dictionary);
// Now write the header file.
writeHeaderFile(message_file, reader.getPrefix(), reader.getNamespace(),
mi_name, &dictionary);
// Finally, warn of any duplicates encountered.
warnDuplicates(reader);
......@@ -573,9 +522,9 @@ main(int argc, char** argv) {
text = isc::strutil::format(text, e.arguments());
cerr << text << "\n";
return (1);
return 1;
}
return (0);
return 0;
}
......@@ -26,10 +26,5 @@ MessageInitializer::MessageInitializer(const char* values[]) {
global->load(values);
}
// Dummy constructor for the MessageInstantiator
MessageInstantiator::MessageInstantiator(MessageInitializer*) {
}
} // namespace log
} // namespace isc
......@@ -55,37 +55,6 @@ public:
MessageInitializer(const char* values[]);
};
/// \brief Instantiate Message Initializer
///
/// A problem with the MessageInitializer class is that an instance of it is
/// created in an external file and initialization of a set of messages requires
/// that that file be included in the link. Unfortunately, if there is no
/// reference to the MessageInitializer object, we cannot guarantee that that
/// will be the case.\n
/// \n
/// The MessageInitializer object is created as a global object, so in theory
/// an "extern" reference to it should work. However, that reference may well
/// be optimised away. To overcome this, the MessageInstantiator class is
/// used.\n
/// \n
/// In the message header file, an instance of MessageInstantiator is created
/// that takes the extern reference to the MessageInitializer as its constructor
/// argument. The constructor - declared in another file - is a no-op. But as
/// the linker doesn't know, it must resolve the reference, hence pulling in the
/// file containing the MessageInitializer.
///\n
/// Note that there is no problem about the static initialization fiasco here -
/// a pointer to the MessageInitializer is passed to the MessageInstantiator,
/// not the object itself; at the MessageInstantiator does nothing with the
/// pointer and does not touch the MessageInitializer. So it doesn't matter
/// whether or not the MessageInitializer's constructor has been called when the
/// MessageInstantiator runs.
class MessageInstantiator {
public:
MessageInstantiator(MessageInitializer* dummy);
};
} // namespace log
} // namespace isc
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <string.h>
#include <log/message_types.h>
namespace isc {
namespace log {
// Compare MessageID for Equality
bool equalMessageID(const MessageID& m1, const MessageID& m2) {
// Attempt to optimise the check. If both are the same instance of the
// message ID, the check for addresses will successed. If both are the
// same message ID but declared in a separate files (so different
// addresses but pointing to the same string), the string equality check
// will work.
return ((m1 == m2) || (strcmp(m1, m2) == 0));
}
} // namespace log
} // namespace isc
// File created from messagedef.mes on Tue Feb 8 18:01:54 2011
// File created from messagedef.mes on Wed Feb 9 10:11:36 2011
#include <cstddef>
#include <log/message_types.h>
#include <log/message_initializer.h>
namespace isc {
namespace log {
extern const isc::log::MessageID MSG_DUPLNS = "DUPLNS";
extern const isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
extern const isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
extern const isc::log::MessageID MSG_NSEXTRARG = "NSEXTRARG";
extern const isc::log::MessageID MSG_NSINVARG = "NSINVARG";
extern const isc::log::MessageID MSG_NSNOARG = "NSNOARG";
extern const isc::log::MessageID MSG_ONETOKEN = "ONETOKEN";
extern const isc::log::MessageID MSG_OPENIN = "OPENIN";
extern const isc::log::MessageID MSG_OPENOUT = "OPENOUT";
extern const isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
extern const isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
extern const isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
extern const isc::log::MessageID MSG_READERR = "READERR";
extern const isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
extern const isc::log::MessageID MSG_WRITERR = "WRITERR";
} // namespace log
} // namespace isc
namespace {
const char* values[] = {
......@@ -24,12 +47,7 @@ const char* values[] = {
NULL
};
} // Anonymous namespace
namespace isc {
namespace log {
isc::log::MessageInitializer initializer(values);
MessageInitializer messagedef_cc_Tue_Feb__8_18_01_54_2011(values);
} // Anonymous namespace
} // namespace log
} // namespace isc
// File created from messagedef.mes on Tue Feb 8 18:01:54 2011
// File created from messagedef.mes on Wed Feb 9 10:11:36 2011
#ifndef __MESSAGEDEF_H
#define __MESSAGEDEF_H
#include <log/message_types.h>
#include <log/message_initializer.h>
namespace isc {
namespace log {
static const isc::log::MessageID MSG_DUPLNS = "DUPLNS";
static const isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
static const isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
static const isc::log::MessageID MSG_NSEXTRARG = "NSEXTRARG";
static const isc::log::MessageID MSG_NSINVARG = "NSINVARG";
static const isc::log::MessageID MSG_NSNOARG = "NSNOARG";
static const isc::log::MessageID MSG_ONETOKEN = "ONETOKEN";
static const isc::log::MessageID MSG_OPENIN = "OPENIN";
static const isc::log::MessageID MSG_OPENOUT = "OPENOUT";
static const isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
static const isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
static const isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
static const isc::log::MessageID MSG_READERR = "READERR";
static const isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
static const isc::log::MessageID MSG_WRITERR = "WRITERR";
extern const isc::log::MessageID MSG_DUPLNS;
extern const isc::log::MessageID MSG_DUPLPRFX;
extern const isc::log::MessageID MSG_IDNOTFND;
extern const isc::log::MessageID MSG_NSEXTRARG;
extern const isc::log::MessageID MSG_NSINVARG;
extern const isc::log::MessageID MSG_NSNOARG;
extern const isc::log::MessageID MSG_ONETOKEN;
extern const isc::log::MessageID MSG_OPENIN;
extern const isc::log::MessageID MSG_OPENOUT;
extern const isc::log::MessageID MSG_PRFEXTRARG;
extern const isc::log::MessageID MSG_PRFINVARG;
extern const isc::log::MessageID MSG_PRFNOARG;
extern const isc::log::MessageID MSG_READERR;
extern const isc::log::MessageID MSG_UNRECDIR;
extern const isc::log::MessageID MSG_WRITERR;
} // namespace log
} // namespace isc
namespace isc {
namespace log {
// The next two objects are needed to bring the default message
// definitions into the program. They make sure that the file
// containing the message text is included in the link process.
//
// The objects are uniquely named (with file name and date and
// time of compilation) to avoid clashes with other objects of
// the same type, either by another #include or as a global
// symbol in another module.
extern MessageInitializer messagedef_cc_Tue_Feb__8_18_01_54_2011;
static MessageInstantiator instantiate_messagedef_cc_Tue_Feb__8_18_01_54_2011(
&messagedef_cc_Tue_Feb__8_18_01_54_2011);
} // namespace log
} // namespace isc
#endif // __MESSAGEDEF_H
......@@ -22,8 +22,6 @@ run_unittests_SOURCES += message_dictionary_unittest.cc
run_unittests_SOURCES += message_reader_unittest.cc
run_unittests_SOURCES += message_initializer_unittest.cc
run_unittests_SOURCES += message_initializer_unittest_2.cc
run_unittests_SOURCES += message_types_unittest.cc
run_unittests_SOURCES += message_types_unittest_2.cc
run_unittests_SOURCES += strutil_unittest.cc
# run_unittests_SOURCES += xdebuglevel_unittest.cc
run_unittests_SOURCES += run_unittests.cc
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above