From 7a23e4d8cac745b376c527531d9f452714849b48 Mon Sep 17 00:00:00 2001 From: Evan Shaw Date: Fri, 25 Sep 2015 10:29:05 +1200 Subject: [PATCH] New amalgamation target This commit adds two new targets to the Makefile: rocksdb.cc and rocksdb.h These files, when combined with the c.h header, are a self-contained RocksDB source distribution called an amalgamation. (The name comes from SQLite's, which is similar in concept.) The main benefit of an amalgamation is that it's very easy to drop into a new project. It also compiles faster compared to compiling individual source files and potentially gives the compiler more opportunity to make optimizations since it can see all functions at once. rocksdb.cc and rocksdb.h are generated by a new script, amalgamate.py. A detailed description of how amalgamate.py works is in a comment at the top of the file. There are also some small changes to existing files to enable the amalgamation: * Use quotes for includes in unity build * Fix an old header inclusion in util/xfunc.cc * Move some includes outside ifdef in util/env_hdfs.cc * Separate out tool sources in Makefile so they won't be included in unity.cc * Unity build now produces a static library Closes #733 --- .gitignore | 4 +- Makefile | 12 +++-- build_tools/amalgamate.py | 110 ++++++++++++++++++++++++++++++++++++++ src.mk | 8 +-- util/env_hdfs.cc | 8 +-- util/xfunc.cc | 1 - 6 files changed, 130 insertions(+), 13 deletions(-) create mode 100755 build_tools/amalgamate.py diff --git a/.gitignore b/.gitignore index 7072f3493..6a92b5d53 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,7 @@ coverage/COVERAGE_REPORT .gdbhistory package/ .phutil_module_cache -unity +unity.a tags rocksdb_dump rocksdb_undump @@ -52,6 +52,8 @@ java/include/org_rocksdb_*.h .idea/ *.iml +rocksdb.cc +rocksdb.h unity.cc java/crossbuild/.vagrant .vagrant/ diff --git a/Makefile b/Makefile index c96e61cd0..da0541235 100644 --- a/Makefile +++ b/Makefile @@ -206,6 +206,7 @@ util/build_version.cc: FORCE else mv -f $@-t $@; fi LIBOBJECTS = $(LIB_SOURCES:.cc=.o) +LIBOBJECTS += $(TOOL_SOURCES:.cc=.o) MOCKOBJECTS = $(MOCK_SOURCES:.cc=.o) GTEST = $(GTEST_DIR)/gtest/gtest-all.o @@ -592,14 +593,17 @@ CLEAN_FILES += unity.cc unity.cc: Makefile rm -f $@ $@-t for source_file in $(LIB_SOURCES); do \ - echo "#include <$$source_file>" >> $@-t; \ + echo "#include \"$$source_file\"" >> $@-t; \ done - echo 'int main(int argc, char** argv){ return 0; }' >> $@-t chmod a=r $@-t mv $@-t $@ -unity: unity.o - $(AM_LINK) +unity.a: unity.o + $(AM_V_AR)rm -f $@ + $(AM_V_at)$(AR) $(ARFLAGS) $@ unity.o + +rocksdb.h rocksdb.cc: build_tools/amalgamate.py Makefile $(LIB_SOURCES) unity.cc + build_tools/amalgamate.py -I. -i./include unity.cc -x include/rocksdb/c.h -H rocksdb.h -o rocksdb.cc clean: rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(LIBRARY) $(SHARED) diff --git a/build_tools/amalgamate.py b/build_tools/amalgamate.py new file mode 100755 index 000000000..548b1e8ce --- /dev/null +++ b/build_tools/amalgamate.py @@ -0,0 +1,110 @@ +#!/usr/bin/python + +# amalgamate.py creates an amalgamation from a unity build. +# It can be run with either Python 2 or 3. +# An amalgamation consists of a header that includes the contents of all public +# headers and a source file that includes the contents of all source files and +# private headers. +# +# This script works by starting with the unity build file and recursively expanding +# #include directives. If the #include is found in a public include directory, +# that header is expanded into the amalgamation header. +# +# A particular header is only expanded once, so this script will +# break if there are multiple inclusions of the same header that are expected to +# expand differently. Similarly, this type of code causes issues: +# +# #ifdef FOO +# #include "bar.h" +# // code here +# #else +# #include "bar.h" // oops, doesn't get expanded +# // different code here +# #endif +# +# The solution is to move the include out of the #ifdef. + +from __future__ import print_function + +import argparse +from os import path +import re +import sys + +include_re = re.compile('^[ \t]*#include[ \t]+"(.*)"[ \t]*$') +included = set() +excluded = set() + +def find_header(name, abs_path, include_paths): + samedir = path.join(path.dirname(abs_path), name) + if path.exists(samedir): + return samedir + for include_path in include_paths: + include_path = path.join(include_path, name) + if path.exists(include_path): + return include_path + return None + +def expand_include(include_path, f, abs_path, source_out, header_out, include_paths, public_include_paths): + if include_path in included: + return False + + included.add(include_path) + with open(include_path) as f: + print('#line 1 "{}"'.format(include_path), file=source_out) + process_file(f, include_path, source_out, header_out, include_paths, public_include_paths) + return True + +def process_file(f, abs_path, source_out, header_out, include_paths, public_include_paths): + for (line, text) in enumerate(f): + m = include_re.match(text) + if m: + filename = m.groups()[0] + # first check private headers + include_path = find_header(filename, abs_path, include_paths) + if include_path: + if include_path in excluded: + source_out.write(text) + expanded = False + else: + expanded = expand_include(include_path, f, abs_path, source_out, header_out, include_paths, public_include_paths) + else: + # now try public headers + include_path = find_header(filename, abs_path, public_include_paths) + if include_path: + # found public header + expanded = False + if include_path in excluded: + source_out.write(text) + else: + expand_include(include_path, f, abs_path, header_out, None, public_include_paths, []) + else: + sys.exit("unable to find {}, included in {} on line {}".format(filename, abs_path, line)) + + if expanded: + print('#line {} "{}"'.format(line+1, abs_path), file=source_out) + elif text != "#pragma once\n": + source_out.write(text) + +def main(): + parser = argparse.ArgumentParser(description="Transform a unity build into an amalgamation") + parser.add_argument("source", help="source file") + parser.add_argument("-I", action="append", dest="include_paths", help="include paths for private headers") + parser.add_argument("-i", action="append", dest="public_include_paths", help="include paths for public headers") + parser.add_argument("-x", action="append", dest="excluded", help="excluded header files") + parser.add_argument("-o", dest="source_out", help="output C++ file", required=True) + parser.add_argument("-H", dest="header_out", help="output C++ header file", required=True) + args = parser.parse_args() + + include_paths = list(map(path.abspath, args.include_paths or [])) + public_include_paths = list(map(path.abspath, args.public_include_paths or [])) + excluded.update(map(path.abspath, args.excluded or [])) + filename = args.source + abs_path = path.abspath(filename) + with open(filename) as f, open(args.source_out, 'w') as source_out, open(args.header_out, 'w') as header_out: + print('#line 1 "{}"'.format(filename), file=source_out) + print('#include "{}"'.format(header_out.name), file=source_out) + process_file(f, abs_path, source_out, header_out, include_paths, public_include_paths) + +if __name__ == "__main__": + main() diff --git a/src.mk b/src.mk index 9e86c01f7..4a5fd335f 100644 --- a/src.mk +++ b/src.mk @@ -130,8 +130,6 @@ LIB_SOURCES = \ utilities/write_batch_with_index/write_batch_with_index.cc \ utilities/write_batch_with_index/write_batch_with_index_internal.cc \ util/event_logger.cc \ - util/ldb_cmd.cc \ - util/ldb_tool.cc \ util/log_buffer.cc \ util/logging.cc \ util/memenv.cc \ @@ -146,7 +144,6 @@ LIB_SOURCES = \ util/rate_limiter.cc \ util/skiplistrep.cc \ util/slice.cc \ - util/sst_dump_tool.cc \ util/statistics.cc \ util/status.cc \ util/status_message.cc \ @@ -162,6 +159,11 @@ LIB_SOURCES = \ util/xfunc.cc \ util/xxhash.cc \ +TOOL_SOURCES = \ + util/ldb_cmd.cc \ + util/ldb_tool.cc \ + util/sst_dump_tool.cc \ + MOCK_SOURCES = \ table/mock_table.cc \ util/mock_env.cc diff --git a/util/env_hdfs.cc b/util/env_hdfs.cc index 77055aafb..30e796245 100644 --- a/util/env_hdfs.cc +++ b/util/env_hdfs.cc @@ -3,6 +3,10 @@ // LICENSE file in the root directory of this source tree. An additional grant // of patent rights can be found in the PATENTS file in the same directory. // + +#include "rocksdb/env.h" +#include "hdfs/env_hdfs.h" + #ifdef USE_HDFS #ifndef ROCKSDB_HDFS_FILE_C #define ROCKSDB_HDFS_FILE_C @@ -13,9 +17,7 @@ #include #include #include -#include "rocksdb/env.h" #include "rocksdb/status.h" -#include "hdfs/env_hdfs.h" #define HDFS_EXISTS 0 #define HDFS_DOESNT_EXIST -1 @@ -598,8 +600,6 @@ Status HdfsEnv::NewLogger(const std::string& fname, #else // USE_HDFS // dummy placeholders used when HDFS is not available -#include "rocksdb/env.h" -#include "hdfs/env_hdfs.h" namespace rocksdb { Status HdfsEnv::NewSequentialFile(const std::string& fname, unique_ptr* result, diff --git a/util/xfunc.cc b/util/xfunc.cc index d80565247..98de1c594 100644 --- a/util/xfunc.cc +++ b/util/xfunc.cc @@ -10,7 +10,6 @@ #include "db/write_callback.h" #include "rocksdb/db.h" #include "rocksdb/options.h" -#include "rocksdb/utilities/optimistic_transaction.h" #include "rocksdb/utilities/optimistic_transaction_db.h" #include "rocksdb/write_batch.h" #include "util/xfunc.h"