Index: streamexecutor/CMakeLists.txt =================================================================== --- streamexecutor/CMakeLists.txt +++ streamexecutor/CMakeLists.txt @@ -2,6 +2,7 @@ option(STREAM_EXECUTOR_UNIT_TESTS "enable unit tests" ON) option(STREAM_EXECUTOR_ENABLE_DOXYGEN "enable StreamExecutor doxygen" ON) +option(STREAM_EXECUTOR_ENABLE_CONFIG_TOOL "enable building streamexecutor-config tool" ON) # First find includes relative to the streamexecutor top-level source path. include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include) @@ -64,6 +65,10 @@ add_subdirectory(lib) add_subdirectory(examples) +if(STREAM_EXECUTOR_ENABLE_CONFIG_TOOL ) + add_subdirectory(tools/streamexecutor-config) +endif(STREAM_EXECUTOR_ENABLE_CONFIG_TOOL ) + install(DIRECTORY include/ DESTINATION include) if (STREAM_EXECUTOR_ENABLE_DOXYGEN) Index: streamexecutor/Doxyfile.in =================================================================== --- streamexecutor/Doxyfile.in +++ streamexecutor/Doxyfile.in @@ -794,7 +794,7 @@ # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = */examples/* */unittests/* +EXCLUDE_PATTERNS = */examples/* */tools/* */unittests/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the Index: streamexecutor/tools/streamexecutor-config/CMakeLists.txt =================================================================== --- /dev/null +++ streamexecutor/tools/streamexecutor-config/CMakeLists.txt @@ -0,0 +1,3 @@ +find_package(PythonInterp REQUIRED) +configure_file(streamexecutor-config.in streamexecutor-config) +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/streamexecutor-config DESTINATION bin) Index: streamexecutor/tools/streamexecutor-config/streamexecutor-config.in =================================================================== --- /dev/null +++ streamexecutor/tools/streamexecutor-config/streamexecutor-config.in @@ -0,0 +1,206 @@ +#!@PYTHON_EXECUTABLE@ +# +#===- streamexecutor-config - Build config script for SE -----*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +r""" +Get configuration info needed to compile programs which use StreamExecutor. + +Runs llvm-config and adds StreamExecutor-specific flags to the output. Supports +only the subset of llvm-config flags that are relevant for applications +compiling against StreamExecutor. + +This utility will typically be used to construct a compile command line for an +application which depends on the StreamExecutor library. + +For example: + c++ example.cpp -o example \ + $(streamexecutor-config \ + --cppflags --cxxflags --ldflags --libs --system-libs) +""" + +import argparse +import errno +import os +import shlex +import subprocess +import sys + +def get_llvm_config_dir(): + """Gets the path to the llvm-config executable. + + Surrounding the cmake replacement with triple quotes should allow for paths + that contain quotes.""" + return r"""@LLVM_BINARY_DIR@/bin""" + +def get_cmake_install_prefix(): + """Gets the value of the cmake variable CMAKE_INSTALL_PREFIX. + + Surrounding the cmake replacement with triple quotes should allow for paths + that contain quotes.""" + return r"""@CMAKE_INSTALL_PREFIX@""" + +def cuddle_flag(flag, tokens): + """If flag appears by itself in tokens, combines it with the next token. + + >>> tokens = ['-I', '/usr/include'] + >>> cuddle_flag('-I', tokens) + >>> tokens + ['-I/usr/include'] + + >>> tokens = ['-L', '/usr/lib'] + >>> cuddle_flag('-L', tokens) + >>> tokens + ['-L/usr/lib'] + + >>> tokens = ['-I'] + >>> cuddle_flag('-I', tokens) + >>> tokens + ['-I'] + + >>> tokens = ['-I', '/usr/include', '-I', '/usr/local/include'] + >>> cuddle_flag('-I', tokens) + >>> tokens + ['-I/usr/include', '-I/usr/local/include'] + """ + start = 0 + while True: + try: + index = tokens.index(flag, start) + except ValueError: + return + if index + 1 < len(tokens): + follower = tokens.pop(index + 1) + tokens[index] = flag + follower + start = index + 1 + +def get_llvm_config_output_for_dir(llvm_config_dir, flags_string): + """Calls llvm-config at the given path and returns the output with -I and -L + flags cuddled.""" + output = subprocess.check_output( + ['%s/llvm-config' % llvm_config_dir] + flags_string.split()).strip() + tokens = shlex.split(output) + cuddle_flag('-I', tokens) + cuddle_flag('-L', tokens) + return ' '.join(tokens) + +def has_token(token, string): + """Checks if the given token appears in the string. + + The token argument must be a single shell token. + + >>> string = '-I/usr/include -L"/usr/lib"' + >>> has_token('-I/usr/include', string) + True + >>> has_token('-I/usr/local/include', string) + False + >>> has_token('-I"/usr/include"', string) + True + >>> has_token('-L"/usr/lib"', string) + True + >>> has_token('-L/usr/lib', string) + True + """ + split_token = shlex.split(token) + if len(split_token) > 1: + raise ValueError('has_token called with a multi-token token: ' + token) + escaped_token = split_token[0] + return escaped_token in shlex.split(string) + +def main(): + parser = argparse.ArgumentParser( + prog='streamexecutor-config', + formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__) + + parser.add_argument( + '--cppflags', + action='store_true', + help= + 'C preprocessor flags for files that include StreamExecutor headers.') + + parser.add_argument( + '--cxxflags', + action='store_true', + help='C++ compiler flags for files that include StreamExecutor headers.') + + parser.add_argument( + '--ldflags', + action='store_true', + help='Print linker flags.') + + parser.add_argument( + '--libs', + action='store_true', + help='Libraries needed to link against StreamExecutor.') + + parser.add_argument( + '--system-libs', + action='store_true', + help='System libraries needed to link against StreamExecutor.') + + parser.add_argument( + '--llvm-config-dir', + default=get_llvm_config_dir(), + help='Directory containing the llvm-config executable. '\ + 'If not specified, defaults to the cmake-configured location') + + args = parser.parse_args() + + # Print the help message if the user did not pass any flag arguments. + if not any( + getattr(args, flag) + for flag in ('cppflags', 'cxxflags', 'ldflags', 'libs', 'system_libs')): + parser.print_help() + sys.exit(1) + + # Check for the presence of the llvm-config executable. + if not os.path.isfile('%s/llvm-config' % args.llvm_config_dir): + sys.exit('llvm-config not found in: ' + args.llvm_config_dir) + if not os.access('%s/llvm-config' % args.llvm_config_dir, os.X_OK): + sys.exit('llvm-config not executable in: ' + args.llvm_config_dir) + + # We will always use args.llvm_config_dir as the second argument to + # get_llvm_config_output_for_path. + get_llvm_config_output = lambda flags : get_llvm_config_output_for_dir( + args.llvm_config_dir, flags) + + all_flags = [] + + if args.cppflags: + llvm_flags = get_llvm_config_output('--cppflags') + all_flags.append(llvm_flags) + se_flag = "-I%s/include" % get_cmake_install_prefix() + if not has_token(token=se_flag, string=llvm_flags): + all_flags.append(se_flag) + + if args.cxxflags: + all_flags.append(get_llvm_config_output('--cxxflags')) + + if args.ldflags: + llvm_flags = get_llvm_config_output('--ldflags') + all_flags.append(llvm_flags) + se_flag = "-L%s/lib" % get_cmake_install_prefix() + if not has_token(token=se_flag, string=llvm_flags): + all_flags.append(se_flag) + + if args.libs: + llvm_flags = get_llvm_config_output('--libs support symbolize') + se_flag = '-lstreamexecutor' + if not has_token(token=se_flag, string=llvm_flags): + all_flags.append(se_flag) + all_flags.append(llvm_flags) + + if args.system_libs: + all_flags.append(get_llvm_config_output('--system-libs')) + + print(' '.join(all_flags)) + +if __name__ == '__main__': + main()