Index: CMakeLists.txt =================================================================== --- /dev/null +++ CMakeLists.txt @@ -0,0 +1,73 @@ +cmake_minimum_required(VERSION 2.8.12.2) + +# The test-suite is designed to be built in release mode anyway and +# falls over unless -DNDEBUG is set. +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "No build type selected, defaulting to Release") + set(CMAKE_BUILD_TYPE "Release") +endif() + +add_definitions(-DNDEBUG) + +# Let's make sure lit output is pretty. +if(CMAKE_VERSION VERSION_LESS 3.1.20141117) + set(cmake_3_2_USES_TERMINAL) +else() + set(cmake_3_2_USES_TERMINAL USES_TERMINAL) +endif() + +project(test-suite C CXX) + +# Add path for custom modules +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" + ) + +# Sanity check our source directory to make sure that we are not trying to +# generate an in-tree build (unless on MSVC_IDE, where it is ok), and to make +# sure that we don't have any stray generated files lying around in the tree +# (which would end up getting picked up by header search, instead of the correct +# versions). +if( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE ) + message(FATAL_ERROR "In-source builds are not allowed. +CMake would overwrite the makefiles distributed with LLVM. +Please create a directory and run cmake from there, passing the path +to this source directory as the last argument. +This process created the file `CMakeCache.txt' and the directory `CMakeFiles'. +Please delete them.") +endif() + +include(MakefileFunctions) +include(SingleMultiSource) +find_package(YACC) +find_package(TCL) + +# FIXME: Detect host architecture, endianness and OS +if(NOT DEFINED TARGET_OS) + message(STATUS "Check target operating system - ${CMAKE_SYSTEM_NAME}") + set(TARGET_OS ${CMAKE_SYSTEM_NAME}) +endif() +if(NOT DEFINED ARCH) + include(DetectArchitecture) + detect_architecture(ARCH) +endif() +if(NOT DEFINED ENDIAN) + include(TestBigEndian) + test_big_endian(IS_BIGENDIAN) + if(IS_BIGENDIAN) + set(ENDIAN "big") + else() + set(ENDIAN "little") + endif() +endif() + +add_subdirectory(tools) + +# Now that the tools have been created, +set(CMAKE_C_COMPILE_OBJECT "${CMAKE_BINARY_DIR}/tools/timeit --summary <OBJECT>.time ${CMAKE_C_COMPILE_OBJECT}") +set(CMAKE_CXX_COMPILE_OBJECT "${CMAKE_BINARY_DIR}/tools/timeit --summary <OBJECT>.time ${CMAKE_CXX_COMPILE_OBJECT}") + +add_subdirectory(SingleSource) +add_subdirectory(MultiSource) Index: cmake/Makefile2CMakeLists.py =================================================================== --- /dev/null +++ cmake/Makefile2CMakeLists.py @@ -0,0 +1,337 @@ +##===- Makefile2CMakeLists.py ---------------------------------------------===## +# +# This script parses Makefiles in the SingleSource and MultiSource directories +# and generates equivalent CMakeLists.txt files. +# +# This script is intended to be run as a one-shot process, with its generated +# CMakeLists.txt checked in to the tree. Once this is done and dusted, this +# script can be deleted from the tree as it serves no more useful purpose. +# +# This script requires `pymake`: https://github.com/mozilla/pymake +# This is not available on PyPI so will need to be downloaded manually and +# `pip install -e`'d. +# +# Usage: cd $TEST_SUITE_ROOT; python Makefile2CMakeLists.py +# +# Improvements required before prime-time: +# * Copy across comments from the Makefiles into the generated CMakeLists. +##===----------------------------------------------------------------------===## + +import os, sys, fnmatch, re + +from pymake.parserdata import * +from pymake.parser import parsefile, parsemakesyntax, Data +import pymake.data as data +import pymake.functions as functions + +class Stream(object): + def __init__(self): + self.s = "" + self._indent = 0 + self.tmp = 0 + def writeLine(self, line): + self.s += ' ' * self._indent + self.s += line + "\n" + def indent(self, i): + self._indent += i + def str(self): + return self.s + def tempvar(self): + self.tmp += 1 + return "TMP%s" % self.tmp + +def _deref(s): + return "${%s}" % s + +def _deescape_in_string(s): + # This is particularly targetted at the escape in lua/Makefile :( + s = re.sub(r'"\\<in', r'"', s) + # And this one is targetted at FreeBench :( + s = s.replace(r'\"', r'\\"') + return s + +def generate_Expansion(e, outstream): + if e.__class__ == data.StringExpansion: + s = e.s + s = s.replace('y.tab.c', '${CMAKE_CURRENT_BINARY_DIR}/y.tab.c') + s = s.replace('awk.tab.c', '${CMAKE_CURRENT_BINARY_DIR}/awk.tab.c') + # Make sure we properly escape backslashes + return '%s' % _deescape_in_string(s) + if e.__class__ == data.Expansion: + s = [] + for e2, isfunc in e: + if isfunc: + s.append(generate_Expansion(e2, outstream)) + else: + assert isinstance(e2, basestring) + s.append('%s' % e2.replace('\\','\\\\')) + return ''.join(s) + if e.__class__ == functions.VariableRef: + # Special case for PROJ_SRC_ROOT and PROJ_SRC_DIR + if e.vname.s == "PROJ_SRC_ROOT": + return '${CMAKE_SOURCE_DIR}' + if e.vname.s == "PROJ_SRC_DIR": + return '${CMAKE_CURRENT_SOURCE_DIR}' + if e.vname.s == "PROJ_OBJ_DIR": + return '${CMAKE_CURRENT_BINARY_DIR}' + if e.vname.s == "SourceDir": + return '${CMAKE_CURRENT_SOURCE_DIR}' + return '${%s}' % e.vname.s + if e.__class__ == functions.ShellFunction: + args = generate_Expansion(e._arguments[0], outstream) + t = outstream.tempvar() + outstream.writeLine('execute_process(COMMAND %s OUTPUT_VARIABLE %s)' % (args, t)) + return t + if e.__class__ == functions.WildcardFunction: + args = generate_Expansion(e._arguments[0], outstream) + t = outstream.tempvar() + outstream.writeLine('file(GLOB %s %s)' % (t, args)) + return _deref(t) + if e.__class__ == functions.AddPrefixFunction: + args = generate_Expansion(e._arguments[1], outstream) + t = outstream.tempvar() + prefix = generate_Expansion(e._arguments[0], outstream) + # Comma-separated list! + args = args.replace(', ', ' ') + outstream.writeLine('set(%s %s)' % (t, args)) + outstream.writeLine('llvm_prepend(%s %s %s)' % (t, prefix, _deref(t))) + return _deref(t) + if e.__class__ == functions.SubstFunction: + s = generate_Expansion(e._arguments[0], outstream) + r = generate_Expansion(e._arguments[1], outstream) + d = generate_Expansion(e._arguments[2], outstream) + t = outstream.tempvar() + outstream.writeLine('string(REPLACE %s %s %s %s)' % (s, r, t, d)) + return _deref(t) + if e.__class__ == functions.FilteroutFunction: + source = generate_Expansion(e._arguments[0], outstream) + out = generate_Expansion(e._arguments[1], outstream) + t = outstream.tempvar() + outstream.writeLine('llvm_filter_out(%s %s %s)' % (t, source, out)) + return _deref(t) + if e.__class__ == functions.FilterFunction: + source = generate_Expansion(e._arguments[0], outstream) + out = generate_Expansion(e._arguments[1], outstream) + t = outstream.tempvar() + outstream.writeLine('llvm_filter(%s %s %s)' % (t, source, out)) + return _deref(t) + if e.__class__ == functions.FindstringFunction: + needle = generate_Expansion(e._arguments[0], outstream) + haystack = generate_Expansion(e._arguments[1], outstream) + t = outstream.tempvar() + outstream.writeLine('string(FIND "%s" "%s" %s)' % (haystack, needle, t)) + return _deref(t) + + raise RuntimeError("Unknown expansion class: %s" % e.__class__) + +def generate_Condition(cond, dirname, outstream, elseif): + if cond.__class__ == EqCondition: + s = "if(" if not elseif else "elseif(" + s += "\"" if cond.expected else "NOT \"" + # FIXME: Do proper coercion to string. + s += generate_Expansion(cond.exp1, outstream) + s += "\" STREQUAL \"" + s += generate_Expansion(cond.exp2, outstream) + s += "\")" + outstream.writeLine(s) + outstream.indent(2) + return + if cond.__class__ == IfdefCondition: + assert not elseif + s = "if(" + if not cond.expected: + s += "NOT " + s += "DEFINED " + s += generate_Expansion(cond.exp, outstream) + s += ")" + outstream.writeLine(s) + outstream.indent(2) + return + if cond.__class__ == ElseCondition: + outstream.writeLine("else()") + outstream.indent(2) + return + + raise RuntimeError("Unknown condition! %s" % cond.__class__) + +def generate_SetVariable(cmd, dirname, outstream): + d = Data.fromstring(cmd.value, cmd.valueloc) + e, t, o = parsemakesyntax(d, 0, (), parser.iterdata) + + name = generate_Expansion(cmd.vnameexp, outstream) + exp = generate_Expansion(e, outstream) + if '+' in cmd.token: + outstream.writeLine("list(APPEND %s %s)" % (name, exp)) + else: + outstream.writeLine("set(%s %s)" % (name, exp)) + +def generate_ConditionBlock(cmd, dirname, outstream): + elseif = False + for c, statements in cmd._groups: + generate_Condition(c, dirname, outstream, elseif) + elseif = True + _generate(dirname, statements, outstream) + outstream.indent(-2) + outstream.writeLine("endif()") + +def generate_Rule(cmd, dirname, outstream): + # Burg adds rules for flex/bison. We'll have to add that ourselves. + if 'Burg' in dirname or 'gawk' in dirname: + return + # Lua and sqlite generate input. Have to add that ourselves. + elif 'lua' in dirname or 'sqlite' in dirname or 'pcompress' in dirname or 'consumer-typeset' in dirname: + return + # FIXME: support 'clean' rules! + elif 'JM' in dirname or 'kimwitu++' in dirname or 'mafft' in dirname: + pass + elif 'rawdaudio' in dirname: + # Rawdaudio just has an empty all rule :/ + return + else: + raise RuntimeError("Creating a rule?!") + +def ignore(cmd, dirname, outstream): + pass + +dispatchtable = { + SetVariable: generate_SetVariable, + ConditionBlock: generate_ConditionBlock, + Rule: generate_Rule, + Include: ignore, + Command: ignore, + EmptyDirective: ignore +} + +extra_cmake = { + 'Burg': ''' +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +add_yacc_parser(burg_parser gram.y ${CMAKE_CURRENT_BINARY_DIR}/y.tab.c ${CMAKE_CURRENT_BINARY_DIR}/y.tab.h) + ''', + 'gawk': ''' +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +add_yacc_parser(gawk_parser awk.y ${CMAKE_CURRENT_BINARY_DIR}/awk.tab.c ${CMAKE_CURRENT_BINARY_DIR}/awk.tab.h) + ''', + 'lua': ''' +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generate_inputs.sh + COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/generate_inputs.sh ${CMAKE_CURRENT_BINARY_DIR} + COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/test ${CMAKE_CURRENT_BINARY_DIR} + COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/bench ${CMAKE_CURRENT_BINARY_DIR} + COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/input ${CMAKE_CURRENT_BINARY_DIR} + COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/*.lua ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/generate_inputs.sh + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating lua test inputs") +add_custom_target(lua_input SOURCES ${CMAKE_CURRENT_BINARY_DIR}/generate_inputs.sh) + ''', + 'sqlite3': ''' +if(SMALL_PROBLEM_SIZE) + set(SQLITE_INPUTNAME smalltest) +else() + set(SQLITE_INPUTNAME speedtest) +endif() +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test15.sql + COMMAND ${TCL_TCLSH} ${CMAKE_CURRENT_SOURCE_DIR}/${SQLITE_INPUTNAME}.tcl + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating sqlite test inputs") +add_custom_target(sqlite_input SOURCES ${CMAKE_CURRENT_BINARY_DIR}/test15.sql) + ''', + 'pcompress2': ''' +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${INPUT_FILE} + COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${INPUT_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Copying pcompress2 test input") +add_custom_target(pcompress2_input SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${INPUT_FILE}) + ''', + 'consumer-typeset': ''' +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/data/hyph + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/data/hyph + COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/data/hyph/* ${CMAKE_CURRENT_BINARY_DIR}/data/hyph + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Copying consumer-typeset test inputs") +add_custom_target(consumer-typeset_input SOURCES ${CMAKE_CURRENT_BINARY_DIR}/data/hyph) + ''', + 'nbench': ''' +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/NNET.DAT + COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/NNET.DAT ${CMAKE_CURRENT_BINARY_DIR}/NNET.DAT + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Copying nbench test input") +add_custom_target(nbench_input SOURCES ${CMAKE_CURRENT_BINARY_DIR}/NNET.DAT) + ''' +} + +late_extra_cmake = { + 'lua': 'add_dependencies(lua lua_input)', + 'sqlite3': 'add_dependencies(sqlite3 sqlite_input)', + 'pcompress2': 'add_dependencies(pcompress2 pcompress2_input)', + 'consumer-typeset': 'add_dependencies(consumer-typeset consumer-typeset_input)', + 'nbench': 'add_dependencies(nbench nbench_input)' +} + +def _generate(dirname, commands, outstream): + for c in commands: + if c.__class__ in dispatchtable: + dispatchtable[c.__class__](c, dirname, outstream) + else: + raise RuntimeError("Unknown command: %s" % c.__class__) + +def generate(dirname, commands, macro=None): + fh = open(os.path.join(dirname, 'CMakeLists.txt'), 'w') + fh.write("""# Autogenerated from %s + +set(DIRS) +set(PARALLEL_DIRS) + +""" % (os.path.join(dirname, 'Makefile'),)) + + s = Stream() + _generate(dirname, commands, s) + fh.write(s.str()) + + if os.path.basename(dirname) in extra_cmake: + fh.write(extra_cmake[os.path.basename(dirname)]) + if macro: + fh.write("%s()\n" % macro) + if os.path.basename(dirname) in late_extra_cmake: + fh.write(late_extra_cmake[os.path.basename(dirname)]) + + fh.write(""" +llvm_add_subdirectories(${DIRS} ${PARALLEL_DIRS}) +""") + +def visitSingleSource(dirname): + files = os.listdir(os.path.abspath(dirname)) + + for filename in files: + absfilename = os.path.join(dirname, filename) + if filename == "Makefile": + print "parsing %s/%s..." % (dirname, filename) + commands = parsefile(absfilename) + print "generating %s/CMakeLists.txt" % (dirname,) + generate(dirname, commands, "llvm_singlesource") + + for filename in files: + absfilename = os.path.join(dirname, filename) + if os.path.isdir(absfilename): + visitSingleSource(absfilename) + +def visitMultiSource(dirname): + files = os.listdir(os.path.abspath(dirname)) + + for filename in files: + absfilename = os.path.join(dirname, filename) + if filename == "Makefile": + print "parsing %s/%s..." % (dirname, filename) + commands = parsefile(absfilename) + print "generating %s/CMakeLists.txt" % (dirname,) + generate(dirname, commands, "llvm_multisource") + + + for filename in files: + absfilename = os.path.join(dirname, filename) + if os.path.isdir(absfilename): + visitMultiSource(absfilename) + +if __name__ == "__main__": + visitSingleSource('SingleSource') + visitMultiSource('MultiSource') + Index: cmake/lit-test-template.in =================================================================== --- /dev/null +++ cmake/lit-test-template.in @@ -0,0 +1,3 @@ +; RUN: ${RUNUNDER} ${CMAKE_SOURCE_DIR}/RunSafely.sh -t ${TIMEIT} 7200 ${STDIN_FILENAME} %t ${CMAKE_CURRENT_BINARY_DIR}/${exename} ${RUN_OPTIONS} +; RUN: ${PROGRAM_OUTPUT_FILTER} %t +; RUN: ${DIFFPROG} %t ${REFERENCE_OUTPUT} Index: cmake/modules/DetectArchitecture.c =================================================================== --- /dev/null +++ cmake/modules/DetectArchitecture.c @@ -0,0 +1,23 @@ +#if defined(__aarch64__) +const char *str = "ARCHITECTURE IS AArch64"; +#elif defined(__arm__) +const char *str = "ARCHITECTURE IS ARM"; +#elif defined(__alpha__) +const char *str = "ARCHITECTURE IS Alpha"; +#elif defined(__mips__) +const char *str = "ARCHITECTURE IS Mips"; +#elif defined(__powerpc__) || defined(__ppc__) || defined(__power__) +const char *str = "ARCHITECTURE IS PowerPC"; +#elif defined(__sparc__) +const char *str = "ARCHITECTURE IS Sparc"; +#elif defined(__xcore__) +const char *str = "ARCHITECTURE IS XCore"; +#elif defined(__i386__) || defined(__x86_64__) +const char *str = "ARCHITECTURE IS x86"; +#endif + +int main(int argc, char **argv) { + int require = str[argc]; + (void)argv; + return require; +} Index: cmake/modules/DetectArchitecture.cmake =================================================================== --- /dev/null +++ cmake/modules/DetectArchitecture.cmake @@ -0,0 +1,35 @@ +##===- DetectArchitecture.cmake -------------------------------------------===## +# +# Performs a try_compile to determine the architecture of the target. +# +##===----------------------------------------------------------------------===## + +macro(detect_architecture variable) + try_compile(HAVE_${variable} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/cmake/modules/DetectArchitecture.c + OUTPUT_VARIABLE OUTPUT + COPY_FILE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/DetectArchitecture.bin) + + if(HAVE_${variable}) + file(STRINGS ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/DetectArchitecture.bin + DETECT_ARCH_STRING LIMIT_COUNT 1 REGEX "ARCHITECTURE IS") + if(DETECT_ARCH_STRING) + string(REGEX MATCH "[^ ]*$" DETECT_ARCH_MATCH ${DETECT_ARCH_STRING}) + if(DETECT_ARCH_MATCH) + message(STATUS "Check target system architecture: ${DETECT_ARCH_MATCH}") + set(${variable} ${DETECT_ARCH_MATCH}) + else() + message(SEND_ERROR "Could not detect target system architecture!") + endif() + else() + message(SEND_ERROR "Could not detect target system architecture!") + endif() + else() + message(STATUS "Determine the system architecture - failed") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining the system architecture fialed with the following output:\n${OUTPUT}") + set(${variable}) + endif() + +endmacro(detect_architecture) \ No newline at end of file Index: cmake/modules/FindYACC.cmake =================================================================== --- /dev/null +++ cmake/modules/FindYACC.cmake @@ -0,0 +1,24 @@ +##===- FindYACC.cmake -----------------------------------------------------===## +# +# Defines custom rules for building a YACC parser. Based on FindBISON.cmake. +# +##===----------------------------------------------------------------------===## + +find_program(YACC_EXECUTABLE yacc DOC "Path to the YACC executable") +mark_as_advanced(YACC_EXECUTABLE) + +if(YACC_EXECUTABLE) + + macro(add_yacc_parser target_name input_grammar output_c output_h) + add_custom_command(OUTPUT ${output_c} ${output_h} + COMMAND ${YACC_EXECUTABLE} + ARGS -d -o ${output_c} ${input_grammar} + DEPENDS ${input_grammar} + COMMENT "Building YACC parser ${output_c}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + endmacro(add_yacc_parser) + +endif(YACC_EXECUTABLE) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(YACC DEFAULT_MSG YACC_EXECUTABLE) Index: cmake/modules/MakefileFunctions.cmake =================================================================== --- /dev/null +++ cmake/modules/MakefileFunctions.cmake @@ -0,0 +1,46 @@ +##===- MakefileFunctions.cmake --------------------------------------------===## +# +# Defines functions used within subdirectory CMakeLists.txt, that look a bit +# like standard GNU Make functions. These support the auto-generation of +# CMakeLists.txt from Makefiles, although any use of these should probably be +# looked at and manually translated into more idiomatic CMake. +# +##===----------------------------------------------------------------------===## + +# llvm_add_subdirectories - wrapper around add_subdirectory that can take +# multiple items. +macro(llvm_add_subdirectories) + foreach(V ${ARGV}) + add_subdirectory(${V}) + endforeach(V) +endmacro() + +# llvm_prepend - equivalent to $(addprefix var,prefix). Modifies var in place. +function(llvm_prepend var prefix) + set(listVar "") + foreach(f ${ARGN}) + list(APPEND listVar "${prefix}${f}") + endforeach(f) + set(${var} "${listVar}" PARENT_SCOPE) +endfunction() + +# llvm_filter - equivalent to $(filter needle,haystack). Returns a list containing +# all of the items in ${ARGN} (after needle) that match needle. +macro(llvm_filter output needle) + set(${output}) + foreach(haystack_item ${ARGN}) + foreach(needle_item ${needle}) + if("${haystack_item}" STREQUAL "${needle_item}") + list(APPEND ${output} ${haystack_item}) + endif() + endforeach() + endforeach() +endmacro() + +# llvm_filter_out - equivalent to $(filter-out needle,haystack). Inverse of +# llvm_filter. +macro(llvm_filter_out output needle) + set(${output} "${ARGN}") + llvm_filter(tmp_output ${needle} ${ARGN}) + list(REMOVE_ITEM ${output} ${tmp_output}) +endmacro() Index: cmake/modules/SingleMultiSource.cmake =================================================================== --- /dev/null +++ cmake/modules/SingleMultiSource.cmake @@ -0,0 +1,192 @@ +##===- SingleMultiSource.cmake --------------------------------------------===## +# +# Defines helpers to add executables and tests. The entry points to this +# file are: +# `llvm_singlesource()` and +# `llvm_multisource()` +# +# Each is a macro that uses the environment it was called in to determine +# what to build and how, and generates a test file that can be given to LIT. +# The test file is generated at configure time. +# +##===----------------------------------------------------------------------===## + +# get_unique_exe_name - Given a source file name after which a test should be +# named, create a unique name for the test. Usually this is just the source file +# with the suffix stripped, but in some cases this ends up causing duplicates +# so attempt to make each unique (by adding pathname segments until they become +# unique). +# +# FIXME: Swap this with a simpler procedure to just append a numeral +set_property(GLOBAL PROPERTY registered_executables) +function(get_unique_exe_name new_name main_src) + get_property(registered_executables GLOBAL PROPERTY registered_executables) + + string(REGEX REPLACE ".[cp]+$" "" path ${main_src}) + string(REGEX REPLACE ".*/" "" name ${path}) + list(FIND registered_executables ${name} name_idx) + + if(${name_idx} EQUAL -1) + set(${new_name} ${name} PARENT_SCOPE) + set_property(GLOBAL APPEND PROPERTY registered_executables ${name}) + return() + endif() + + # There is a clash. Rename the target. Each time around the loop pull in + # a new path component. + foreach(n RANGE 1 4) + string(REGEX REPLACE ".*/([^/]+/${name})" "\\1" name ${path}) + string(REGEX REPLACE "/" "-" safe_name ${name}) + + list(FIND registered_executables ${safe_name} name_idx) + if(${name_idx} EQUAL -1) + set(${new_name} ${safe_name} PARENT_SCOPE) + set_property(GLOBAL APPEND PROPERTY registered_executables ${safe_name}) + return() + endif() + endforeach() + message(FATAL_ERROR "Failed to uniquify executable name!") +endfunction() + +# append_cflags - add flags to the CFLAGS for target. +macro(append_cflags target flags) + if(NOT "${${flags}}" STREQUAL "") + get_target_property(old_cflags ${target} COMPILE_FLAGS) + if(${old_cflags} STREQUAL "old_cflags-NOTFOUND") + set(old_cflags) + endif() + string(REPLACE ";" " " s "${old_cflags};${${flags}}") + set_target_properties(${target} PROPERTIES COMPILE_FLAGS ${s}) + endif() +endmacro() + +# append_ldflags - add flags to the LDFLAGS for target. +macro(append_ldflags target flags) + if(NOT "${${flags}}" STREQUAL "") + get_target_property(old_ldflags ${target} LINK_FLAGS) + if(${old_ldflags} STREQUAL "old_ldflags-NOTFOUND") + set(old_ldflags) + endif() + string(REPLACE ";" " " s "${old_ldflags};${${flags}}") + set_target_properties(${target} PROPERTIES LINK_FLAGS ${s}) + endif() +endmacro() + +# llvm_add_test - Create a .test driver file suitable for LIT. +# +# The test template lives in cmake/lit-test-template.in and is configured by this function. +function(llvm_add_test name exename) + if(NOT DEFINED STDIN_FILENAME) + set(STDIN_FILENAME /dev/null) + endif() + + # Hash if we've been asked to, otherwise just use "touch" as an identity function. + if(HASH_PROGRAM_OUTPUT) + set(PROGRAM_OUTPUT_FILTER ${CMAKE_SOURCE_DIR}/HashProgramOutput.sh) + else() + set(PROGRAM_OUTPUT_FILTER touch) + endif() + + # Find the reference output file key name. + if(SMALL_PROBLEM_SIZE) + set(KEY small) + elseif(LARGE_PROBLEM_SIZE) + set(KEY large) + else() + set(KEY) + endif() + + # Pick the best reference output based on "programname.reference_output". + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.reference_output.${ENDIAN}-endian.${KEY}) + set(REFERENCE_OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${name}.reference_output.${ENDIAN}-endian.${KEY}) + elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.reference_output.${KEY}) + set(REFERENCE_OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${name}.reference_output.${KEY}) + elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.reference_output.${ENDIAN}-endian) + set(REFERENCE_OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${name}.reference_output.${ENDIAN}-endian) + elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.reference_output) + set(REFERENCE_OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${name}.reference_output) + else() + # Just compare to its own output. This will always succeed, but here's hoping the + # test in question uses its exit value to determine status, so it'll be caught by + # the previous RUN line. + set(REFERENCE_OUTPUT "%t") + message("-- No reference output found for test ${name}") + endif() + + # If the program is nondeterministic, don't bother diffing and use "touch" again as an identity. + if(DEFINED PROGRAM_IS_NONDETERMINISTIC) + set(DIFFPROG touch) + else() + set(DIFFPROG ${CMAKE_BINARY_DIR}/tools/fpcmp) + if(DEFINED FP_TOLERANCE) + set(DIFFPROG "${DIFFPROG} -r ${FP_TOLERANCE}") + endif() + if(DEFINED FP_ABSTOLERANCE) + set(DIFFPROG "${DIFFPROG} -a ${FP_ABSTOLERANCE}") + endif() + endif() + + if(DEFINED RUN_OPTIONS) + # RUN_OPTIONS is a semicolon-separated list. Change it into a whitespace-separated string. + string(REPLACE ";" " " RUN_OPTIONS "${RUN_OPTIONS}") + endif() + + # FIXME: Sort out how to call timeit, or timeit.sh. + set(TIMEIT ${CMAKE_BINARY_DIR}/tools/timeit) + # FIXME: Add runtimelimit support. + + # Now do the actual configuration. + configure_file(${CMAKE_SOURCE_DIR}/cmake/lit-test-template.in + ${CMAKE_CURRENT_BINARY_DIR}/${exename}.test) +endfunction() + +# llvm_singlesource - configure the current directory as a SingleSource subdirectory - +# i.e. every file in *.{c,cpp,cc} is treated as its own test. +macro(llvm_singlesource) + file(GLOB sources *.c *.cpp *.cc) + foreach(source ${sources}) + # Find the pure name of the test + string(REGEX REPLACE ".[cp]+$" "" path ${source}) + string(REGEX REPLACE ".*/" "" name ${path}) + + # Should we skip this? + list(FIND PROGRAMS_TO_SKIP ${name} name_idx) + if(${name_idx} EQUAL -1) + get_unique_exe_name(source_exename ${source}) + add_executable(${source_exename} ${source}) + append_cflags(${source_exename} CFLAGS) + append_cflags(${source_exename} CPPFLAGS) + append_cflags(${source_exename} CXXFLAGS) + append_ldflags(${source_exename} LDFLAGS) + llvm_add_test(${name} ${source_exename}) + endif() + endforeach() +endmacro() + +# llvm_multisource - configure the current directory as a MultiSource subdirectory - +# i.e. there is one test and it consists of all sources in the directory (or a curated +# list, if Source is defined). +macro(llvm_multisource) + if(DEFINED Source) + set(sources ${Source}) + else() + file(GLOB sources *.c *.cpp *.cc) + endif() + list(LENGTH sources sources_len) + + if(sources_len GREATER 0 AND DEFINED PROG) + # Should we skip this? + list(FIND PROGRAMS_TO_SKIP ${PROG} name_idx) + if(${name_idx} EQUAL -1) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + get_unique_exe_name(source_exename "${PROG}.c") + add_executable(${source_exename} ${sources}) + append_cflags(${source_exename} CFLAGS) + append_cflags(${source_exename} CPPFLAGS) + append_cflags(${source_exename} CXXFLAGS) + append_ldflags(${source_exename} LDFLAGS) + llvm_add_test(${PROG} ${source_exename}) + endif() +endif() +endmacro() Index: lit.cfg =================================================================== --- /dev/null +++ lit.cfg @@ -0,0 +1,46 @@ +import lit.formats +import lit +import os, glob, re + +def getUserTimeFromTimeOutput(f): + with open(f) as fd: + l = [l for l in fd.readlines() + if l.startswith('user')] + assert len(l) == 1 + + m = re.match(r'user\s+([0-9.]+)', l[0]) + return float(m.group(1)) + +class TestSuiteTest(lit.formats.ShTest): + def __init__(self): + lit.formats.ShTest.__init__(self, False) + + def execute(self, test, litConfig): + result = lit.formats.ShTest.execute(self, test, litConfig) + basepath = os.path.dirname(test.getFilePath()) + + if not result.code.isFailure: + # Collect the timing information. + timeglob = os.path.join(basepath, 'Output', '*.time') + times = glob.glob(timeglob) + assert len(times) == 1 + time = getUserTimeFromTimeOutput(times[0]) + + result.addMetric('exec_time', lit.Test.toMetricValue(time)) + + # For completeness, attempt to find compile time information too. + compile_time = 0.0 + for path, subdirs, files in os.walk(basepath): + for file in files: + if file.endswith('.o.time'): + compile_time += getUserTimeFromTimeOutput(os.path.join(path, file)) + result.addMetric('compile_time', lit.Test.toMetricValue(compile_time)) + + return result + +config.name = 'test-suite' + +config.test_format = TestSuiteTest() +config.suffixes = ['.test'] +config.test_source_root = os.path.dirname(__file__) +config.excludes = ['ABI-Testsuite'] Index: tools/CMakeLists.txt =================================================================== --- /dev/null +++ tools/CMakeLists.txt @@ -0,0 +1,5 @@ + +# FIXME: These need to be host-compiled, if we're cross compiling. +# FIXME: Replicate Makefile.tools's logic for determining whether to use fpcmp/fpcmp.sh +add_executable(fpcmp fpcmp.c) +add_executable(timeit timeit.c)