mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-22 13:26:10 +01:00
Provide 1 .hpp + 1 .cpp distribution of Catch2
This commits also adds a script that does the amalgamation of headers and .cpp files into the distributable version, removes the old `generateSingleHeader` script, and also adds a very simple compilation test for the amalgamated distribution.
This commit is contained in:
parent
8b89a60bf6
commit
60dfec559f
@ -174,6 +174,12 @@ matrix:
|
|||||||
before_script:
|
before_script:
|
||||||
- export CXX=${COMPILER}
|
- export CXX=${COMPILER}
|
||||||
- cd ${TRAVIS_BUILD_DIR}
|
- cd ${TRAVIS_BUILD_DIR}
|
||||||
|
# We want to regenerate the amalgamated header if the extra tests
|
||||||
|
# are enabled.
|
||||||
|
- |
|
||||||
|
if [[ ${EXTRAS} -eq 1 ]]; then
|
||||||
|
python3 ./tools/scripts/generateAmalgamatedFiles.py
|
||||||
|
fi
|
||||||
|
|
||||||
- |
|
- |
|
||||||
if [[ ${CPP17} -eq 1 ]]; then
|
if [[ ${CPP17} -eq 1 ]]; then
|
||||||
|
@ -23,6 +23,8 @@ install:
|
|||||||
|
|
||||||
before_build:
|
before_build:
|
||||||
- set CXXFLAGS=%additional_flags%
|
- set CXXFLAGS=%additional_flags%
|
||||||
|
# If we are building examples/extra-tests, we need to regenerate the amalgamated files
|
||||||
|
- cmd: if "%examples%"=="1" ( python .\tools\scripts\generateAmalgamatedFiles.py )
|
||||||
# Indirection because appveyor doesn't handle multiline batch scripts properly
|
# Indirection because appveyor doesn't handle multiline batch scripts properly
|
||||||
# https://stackoverflow.com/questions/37627248/how-to-split-a-command-over-multiple-lines-in-appveyor-yml/37647169#37647169
|
# https://stackoverflow.com/questions/37627248/how-to-split-a-command-over-multiple-lines-in-appveyor-yml/37647169#37647169
|
||||||
# https://help.appveyor.com/discussions/questions/3888-multi-line-cmd-or-powershell-warning-ignore
|
# https://help.appveyor.com/discussions/questions/3888-multi-line-cmd-or-powershell-warning-ignore
|
||||||
|
9429
extras/catch_amalgamated.cpp
Normal file
9429
extras/catch_amalgamated.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10948
extras/catch_amalgamated.hpp
Normal file
10948
extras/catch_amalgamated.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -203,3 +203,24 @@ endforeach()
|
|||||||
list(FILTER EXTRA_TEST_BINARIES EXCLUDE REGEX "DisabledExceptions.*")
|
list(FILTER EXTRA_TEST_BINARIES EXCLUDE REGEX "DisabledExceptions.*")
|
||||||
list(APPEND CATCH_WARNING_TARGETS ${EXTRA_TEST_BINARIES})
|
list(APPEND CATCH_WARNING_TARGETS ${EXTRA_TEST_BINARIES})
|
||||||
set(CATCH_WARNING_TARGETS ${CATCH_WARNING_TARGETS} PARENT_SCOPE)
|
set(CATCH_WARNING_TARGETS ${CATCH_WARNING_TARGETS} PARENT_SCOPE)
|
||||||
|
|
||||||
|
|
||||||
|
# This sets up a one-off executable that compiles against the amalgamated
|
||||||
|
# files, and then runs it for a super simple check that the amalgamated
|
||||||
|
# files are usable.
|
||||||
|
add_executable(AmalgamatedTestCompilation
|
||||||
|
${TESTS_DIR}/X91-AmalgamatedCatch.cpp
|
||||||
|
${CATCH_DIR}/extras/catch_amalgamated.hpp
|
||||||
|
${CATCH_DIR}/extras/catch_amalgamated.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(AmalgamatedTestCompilation PRIVATE ${CATCH_DIR}/extras)
|
||||||
|
set_property( TARGET AmalgamatedTestCompilation PROPERTY CXX_STANDARD 14 )
|
||||||
|
set_property( TARGET AmalgamatedTestCompilation PROPERTY CXX_STANDARD_REQUIRED ON )
|
||||||
|
set_property( TARGET AmalgamatedTestCompilation PROPERTY CXX_EXTENSIONS OFF )
|
||||||
|
|
||||||
|
add_test(NAME AmalgamatedFileTest COMMAND AmalgamatedTestCompilation)
|
||||||
|
set_tests_properties(
|
||||||
|
AmalgamatedFileTest
|
||||||
|
PROPERTIES
|
||||||
|
PASS_REGULAR_EXPRESSION "All tests passed \\(14 assertions in 3 test cases\\)"
|
||||||
|
)
|
||||||
|
31
tests/ExtraTests/X91-AmalgamatedCatch.cpp
Normal file
31
tests/ExtraTests/X91-AmalgamatedCatch.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**\file
|
||||||
|
*
|
||||||
|
* This file serves as a simple compilation test against the amalgamated
|
||||||
|
* version of Catch2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "catch_amalgamated.hpp"
|
||||||
|
|
||||||
|
TEST_CASE("Just a dummy test") {
|
||||||
|
auto i = GENERATE(1, 2, 3);
|
||||||
|
SECTION("a") {
|
||||||
|
REQUIRE(1 != 4);
|
||||||
|
}
|
||||||
|
SECTION("b") {
|
||||||
|
CHECK(1 != 5);
|
||||||
|
}
|
||||||
|
REQUIRE_THAT(1,
|
||||||
|
Catch::Matchers::Predicate<int>([](int i) {
|
||||||
|
return i % 2 == 1;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEMPLATE_TEST_CASE("Trivial template test case", "", char, short) {
|
||||||
|
STATIC_REQUIRE(sizeof(TestType) <= sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Benchmark test", "[!benchmark]") {
|
||||||
|
BENCHMARK("Allocation benchmark") {
|
||||||
|
return std::vector<int>(10);
|
||||||
|
};
|
||||||
|
}
|
119
tools/scripts/generateAmalgamatedFiles.py
Executable file
119
tools/scripts/generateAmalgamatedFiles.py
Executable file
@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from scriptCommon import catchPath
|
||||||
|
from releaseCommon import Version
|
||||||
|
|
||||||
|
root_path = os.path.join(catchPath, 'src')
|
||||||
|
starting_header = os.path.join(root_path, 'catch2', 'catch_all.hpp')
|
||||||
|
output_header = os.path.join(catchPath, 'extras', 'catch_amalgamated.hpp')
|
||||||
|
output_cpp = os.path.join(catchPath, 'extras', 'catch_amalgamated.cpp')
|
||||||
|
|
||||||
|
# These are the copyright comments in each file, we want to ignore them
|
||||||
|
copyright_lines = [
|
||||||
|
'// Copyright Catch2 Authors\n',
|
||||||
|
'// Distributed under the Boost Software License, Version 1.0.\n',
|
||||||
|
'// (See accompanying file LICENSE_1_0.txt or copy at\n',
|
||||||
|
'// https://www.boost.org/LICENSE_1_0.txt)\n',
|
||||||
|
'// SPDX-License-Identifier: BSL-1.0\n',
|
||||||
|
]
|
||||||
|
|
||||||
|
# The header of the amalgamated file: copyright information + explanation
|
||||||
|
# what this file is.
|
||||||
|
file_header = '''\
|
||||||
|
// Copyright Catch2 Authors
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
// https://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
|
// Catch v{version_string}
|
||||||
|
// Generated: {generation_time}
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
// This file is an amalgamation of multiple different files.
|
||||||
|
// You probably shouldn't edit it directly.
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Returns file header with proper version string and generation time
|
||||||
|
def formatted_file_header(version):
|
||||||
|
return file_header.format(version_string=version.getVersionString(),
|
||||||
|
generation_time=datetime.datetime.now())
|
||||||
|
|
||||||
|
# Which headers were already concatenated (and thus should not be
|
||||||
|
# processed again)
|
||||||
|
concatenated_headers = set()
|
||||||
|
|
||||||
|
internal_include_parser = re.compile(r'\s*#include <(catch2/.*)>.*')
|
||||||
|
|
||||||
|
def concatenate_file(out, filename: str, expand_headers: bool) -> int:
|
||||||
|
# Gathers statistics on how many headers were expanded
|
||||||
|
concatenated = 1
|
||||||
|
with open(filename, mode='r', encoding='utf-8') as input:
|
||||||
|
for line in input:
|
||||||
|
if line in copyright_lines:
|
||||||
|
continue
|
||||||
|
m = internal_include_parser.match(line)
|
||||||
|
# anything that isn't a Catch2 header can just be copied to
|
||||||
|
# the resulting file
|
||||||
|
if not m:
|
||||||
|
out.write(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TBD: We can also strip out include guards from our own
|
||||||
|
# headers, but it wasn't worth the time at the time of writing
|
||||||
|
# this script.
|
||||||
|
|
||||||
|
# We do not want to expand headers for the cpp file
|
||||||
|
# amalgamation but neither do we want to copy them to output
|
||||||
|
if not expand_headers:
|
||||||
|
continue
|
||||||
|
|
||||||
|
next_header = m.group(1)
|
||||||
|
# We have to avoid re-expanding the same header over and
|
||||||
|
# over again, or the header will end up with couple
|
||||||
|
# hundred thousands lines (~300k as of preview3 :-) )
|
||||||
|
if next_header in concatenated_headers:
|
||||||
|
continue
|
||||||
|
concatenated_headers.add(next_header)
|
||||||
|
concatenated += concatenate_file(out, os.path.join(root_path, next_header), expand_headers)
|
||||||
|
|
||||||
|
return concatenated
|
||||||
|
|
||||||
|
|
||||||
|
def generate_header():
|
||||||
|
with open(output_header, mode='w', encoding='utf-8') as header:
|
||||||
|
header.write(formatted_file_header(Version()))
|
||||||
|
header.write('#ifndef CATCH_AMALGAMATED_HPP_INCLUDED\n')
|
||||||
|
header.write('#define CATCH_AMALGAMATED_HPP_INCLUDED\n')
|
||||||
|
print('Concatenated {} headers'.format(concatenate_file(header, starting_header, True)))
|
||||||
|
header.write('#endif // CATCH_AMALGAMATED_HPP_INCLUDED\n')
|
||||||
|
|
||||||
|
def generate_cpp():
|
||||||
|
from glob import glob
|
||||||
|
cpp_files = sorted(glob(os.path.join(root_path, 'catch2', '**/*.cpp'), recursive=True))
|
||||||
|
with open(output_cpp, mode='w', encoding='utf-8') as cpp:
|
||||||
|
cpp.write(formatted_file_header(Version()))
|
||||||
|
cpp.write('\n#include "catch_amalgamated.hpp"\n')
|
||||||
|
for file in cpp_files:
|
||||||
|
concatenate_file(cpp, file, False)
|
||||||
|
print('Concatenated {} cpp files'.format(len(cpp_files)))
|
||||||
|
|
||||||
|
|
||||||
|
generate_header()
|
||||||
|
generate_cpp()
|
||||||
|
|
||||||
|
|
||||||
|
# Notes:
|
||||||
|
# * For .cpp files, internal includes have to be stripped and rewritten
|
||||||
|
# * for .hpp files, internal includes have to be resolved and included
|
||||||
|
# * The .cpp file needs to start with `#include "catch_amalgamated.hpp"
|
||||||
|
# * include guards can be left/stripped, doesn't matter
|
||||||
|
# * *.cpp files should be included sorted, to minimize diffs between versions
|
||||||
|
# * *.hpp files should also be somehow sorted -> use catch_all.hpp as the
|
||||||
|
# * entrypoint
|
||||||
|
# * allow disabling main in the .cpp amalgamation
|
@ -1,129 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import os
|
|
||||||
import io
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import datetime
|
|
||||||
from glob import glob
|
|
||||||
|
|
||||||
from scriptCommon import catchPath
|
|
||||||
|
|
||||||
def generate(v):
|
|
||||||
includesParser = re.compile( r'\s*#\s*include\s*"(.*)"' )
|
|
||||||
guardParser = re.compile( r'\s*#.*(TWOBLUECUBES_)?CATCH_.*_INCLUDED')
|
|
||||||
defineParser = re.compile( r'\s*#define\s+(TWOBLUECUBES_)?CATCH_.*_INCLUDED')
|
|
||||||
ifParser = re.compile( r'\s*#ifndef (TWOBLUECUBES_)?CATCH_.*_INCLUDED')
|
|
||||||
endIfParser = re.compile( r'\s*#endif // (TWOBLUECUBES_)?CATCH_.*_INCLUDED')
|
|
||||||
ifImplParser = re.compile( r'\s*#ifdef CATCH_CONFIG_RUNNER' )
|
|
||||||
commentParser1 = re.compile( r'^\s*/\*')
|
|
||||||
commentParser2 = re.compile( r'^ \*')
|
|
||||||
blankParser = re.compile( r'^\s*$')
|
|
||||||
|
|
||||||
seenHeaders = set([])
|
|
||||||
rootPath = os.path.join( catchPath, 'include/' )
|
|
||||||
outputPath = os.path.join( catchPath, 'single_include/catch2/catch.hpp' )
|
|
||||||
|
|
||||||
globals = {
|
|
||||||
'includeImpl' : True,
|
|
||||||
'ifdefs' : 0,
|
|
||||||
'implIfDefs' : -1
|
|
||||||
}
|
|
||||||
|
|
||||||
for arg in sys.argv[1:]:
|
|
||||||
arg = arg.lower()
|
|
||||||
if arg == "noimpl":
|
|
||||||
globals['includeImpl'] = False
|
|
||||||
print( "Not including impl code" )
|
|
||||||
else:
|
|
||||||
print( "\n** Unrecognised argument: " + arg + " **\n" )
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
# ensure that the output directory exists (hopefully no races)
|
|
||||||
outDir = os.path.dirname(outputPath)
|
|
||||||
if not os.path.exists(outDir):
|
|
||||||
os.makedirs(outDir)
|
|
||||||
out = io.open( outputPath, 'w', newline='\n', encoding='utf-8')
|
|
||||||
|
|
||||||
def write( line ):
|
|
||||||
if globals['includeImpl'] or globals['implIfDefs'] == -1:
|
|
||||||
out.write( line )
|
|
||||||
|
|
||||||
def insertCpps():
|
|
||||||
dirs = [os.path.join( rootPath, s) for s in ['', 'internal', 'reporters', 'internal/benchmark', 'internal/benchmark/detail']]
|
|
||||||
cppFiles = []
|
|
||||||
for dir in dirs:
|
|
||||||
cppFiles += glob(os.path.join(dir, '*.cpp'))
|
|
||||||
# To minimize random diffs, sort the files before processing them
|
|
||||||
for fname in sorted(cppFiles):
|
|
||||||
dir, name = fname.rsplit(os.path.sep, 1)
|
|
||||||
dir += os.path.sep
|
|
||||||
parseFile(dir, name)
|
|
||||||
|
|
||||||
def parseFile( path, filename ):
|
|
||||||
f = io.open( os.path.join(path, filename), 'r', encoding='utf-8' )
|
|
||||||
blanks = 0
|
|
||||||
write( u"// start {0}\n".format( filename ) )
|
|
||||||
for line in f:
|
|
||||||
if '// ~*~* CATCH_CPP_STITCH_PLACE *~*~' in line:
|
|
||||||
insertCpps()
|
|
||||||
continue
|
|
||||||
elif ifParser.match( line ):
|
|
||||||
globals['ifdefs'] += 1
|
|
||||||
elif endIfParser.match( line ):
|
|
||||||
globals['ifdefs'] -= 1
|
|
||||||
if globals['ifdefs'] == globals['implIfDefs']:
|
|
||||||
globals['implIfDefs'] = -1
|
|
||||||
m = includesParser.match( line )
|
|
||||||
if m:
|
|
||||||
header = m.group(1)
|
|
||||||
headerPath, sep, headerFile = header.rpartition( "/" )
|
|
||||||
if headerFile not in seenHeaders:
|
|
||||||
if headerFile != "tbc_text_format.h" and headerFile != "clara.h":
|
|
||||||
seenHeaders.add( headerFile )
|
|
||||||
if headerPath == "internal" and path.endswith("internal/"):
|
|
||||||
headerPath = ""
|
|
||||||
sep = ""
|
|
||||||
if os.path.exists( path + headerPath + sep + headerFile ):
|
|
||||||
parseFile( path + headerPath + sep, headerFile )
|
|
||||||
else:
|
|
||||||
parseFile( rootPath + headerPath + sep, headerFile )
|
|
||||||
else:
|
|
||||||
if ifImplParser.match(line):
|
|
||||||
globals['implIfDefs'] = globals['ifdefs']
|
|
||||||
if (not guardParser.match( line ) or defineParser.match( line ) ) and not commentParser1.match( line )and not commentParser2.match( line ):
|
|
||||||
if blankParser.match( line ):
|
|
||||||
blanks = blanks + 1
|
|
||||||
else:
|
|
||||||
blanks = 0
|
|
||||||
if blanks < 2 and not defineParser.match(line):
|
|
||||||
write( line.rstrip() + "\n" )
|
|
||||||
write( u'// end {}\n'.format(filename) )
|
|
||||||
|
|
||||||
|
|
||||||
write( u"/*\n" )
|
|
||||||
write( u" * Catch v{0}\n".format( v.getVersionString() ) )
|
|
||||||
write( u" * Generated: {0}\n".format( datetime.datetime.now() ) )
|
|
||||||
write( u" * ----------------------------------------------------------\n" )
|
|
||||||
write( u" * This file has been merged from multiple headers. Please don't edit it directly\n" )
|
|
||||||
write( u" * Copyright (c) {} Two Blue Cubes Ltd. All rights reserved.\n".format( datetime.date.today().year ) )
|
|
||||||
write( u" *\n" )
|
|
||||||
write( u" * Distributed under the Boost Software License, Version 1.0. (See accompanying\n" )
|
|
||||||
write( u" * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n" )
|
|
||||||
write( u" */\n" )
|
|
||||||
write( u"#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
|
|
||||||
write( u"#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
|
|
||||||
|
|
||||||
parseFile( rootPath, 'catch.hpp' )
|
|
||||||
|
|
||||||
write( u"#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n\n" )
|
|
||||||
out.close()
|
|
||||||
print ("Generated single include for Catch v{0}\n".format( v.getVersionString() ) )
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from releaseCommon import Version
|
|
||||||
generate(Version())
|
|
Loading…
Reference in New Issue
Block a user