Ryan Prichard | 7aea7e9 | 2022-01-13 17:30:17 -0800 | [diff] [blame^] | 1 | # Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| 2 | # file Copyright.txt or https://cmake.org/licensing for details. |
| 3 | |
| 4 | #[=======================================================================[.rst: |
| 5 | FindFLEX |
| 6 | -------- |
| 7 | |
| 8 | Find Fast Lexical Analyzer (Flex) executable and provides a macro |
| 9 | to generate custom build rules |
| 10 | |
| 11 | |
| 12 | |
| 13 | The module defines the following variables: |
| 14 | |
| 15 | :: |
| 16 | |
| 17 | FLEX_FOUND - True is flex executable is found |
| 18 | FLEX_EXECUTABLE - the path to the flex executable |
| 19 | FLEX_VERSION - the version of flex |
| 20 | FLEX_LIBRARIES - The flex libraries |
| 21 | FLEX_INCLUDE_DIRS - The path to the flex headers |
| 22 | |
| 23 | |
| 24 | |
| 25 | The minimum required version of flex can be specified using the |
| 26 | standard syntax, e.g. :command:`find_package(FLEX 2.5.13)` |
| 27 | |
| 28 | |
| 29 | |
| 30 | If flex is found on the system, the module provides the macro: |
| 31 | |
| 32 | :: |
| 33 | |
| 34 | FLEX_TARGET(Name FlexInput FlexOutput |
| 35 | [COMPILE_FLAGS <string>] |
| 36 | [DEFINES_FILE <string>] |
| 37 | ) |
| 38 | |
| 39 | which creates a custom command to generate the ``FlexOutput`` file from |
| 40 | the ``FlexInput`` file. Name is an alias used to get details of this custom |
| 41 | command. If ``COMPILE_FLAGS`` option is specified, the next |
| 42 | parameter is added to the flex command line. |
| 43 | |
| 44 | .. versionadded:: 3.5 |
| 45 | If flex is configured to |
| 46 | output a header file, the ``DEFINES_FILE`` option may be used to specify its |
| 47 | name. |
| 48 | |
| 49 | .. versionchanged:: 3.17 |
| 50 | When :policy:`CMP0098` is set to ``NEW``, ``flex`` runs in the |
| 51 | :variable:`CMAKE_CURRENT_BINARY_DIR` directory. |
| 52 | |
| 53 | The macro defines the following variables: |
| 54 | |
| 55 | :: |
| 56 | |
| 57 | FLEX_${Name}_DEFINED - true is the macro ran successfully |
| 58 | FLEX_${Name}_OUTPUTS - the source file generated by the custom rule, an |
| 59 | alias for FlexOutput |
| 60 | FLEX_${Name}_INPUT - the flex source file, an alias for ${FlexInput} |
| 61 | FLEX_${Name}_OUTPUT_HEADER - the header flex output, if any. |
| 62 | |
| 63 | |
| 64 | |
| 65 | Flex scanners often use tokens defined by Bison: the code generated |
| 66 | by Flex depends of the header generated by Bison. This module also |
| 67 | defines a macro: |
| 68 | |
| 69 | :: |
| 70 | |
| 71 | ADD_FLEX_BISON_DEPENDENCY(FlexTarget BisonTarget) |
| 72 | |
| 73 | which adds the required dependency between a scanner and a parser |
| 74 | where ``FlexTarget`` and ``BisonTarget`` are the first parameters of |
| 75 | respectively ``FLEX_TARGET`` and ``BISON_TARGET`` macros. |
| 76 | |
| 77 | :: |
| 78 | |
| 79 | ==================================================================== |
| 80 | Example: |
| 81 | |
| 82 | |
| 83 | |
| 84 | :: |
| 85 | |
| 86 | find_package(BISON) |
| 87 | find_package(FLEX) |
| 88 | |
| 89 | |
| 90 | |
| 91 | :: |
| 92 | |
| 93 | BISON_TARGET(MyParser parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp) |
| 94 | FLEX_TARGET(MyScanner lexer.l ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp) |
| 95 | ADD_FLEX_BISON_DEPENDENCY(MyScanner MyParser) |
| 96 | |
| 97 | |
| 98 | |
| 99 | :: |
| 100 | |
| 101 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) |
| 102 | add_executable(Foo |
| 103 | Foo.cc |
| 104 | ${BISON_MyParser_OUTPUTS} |
| 105 | ${FLEX_MyScanner_OUTPUTS} |
| 106 | ) |
| 107 | target_link_libraries(Foo ${FLEX_LIBRARIES}) |
| 108 | ==================================================================== |
| 109 | #]=======================================================================] |
| 110 | |
| 111 | find_program(FLEX_EXECUTABLE NAMES flex win-flex win_flex DOC "path to the flex executable") |
| 112 | mark_as_advanced(FLEX_EXECUTABLE) |
| 113 | |
| 114 | find_library(FL_LIBRARY NAMES fl |
| 115 | DOC "Path to the fl library") |
| 116 | |
| 117 | find_path(FLEX_INCLUDE_DIR FlexLexer.h |
| 118 | DOC "Path to the flex headers") |
| 119 | |
| 120 | mark_as_advanced(FL_LIBRARY FLEX_INCLUDE_DIR) |
| 121 | |
| 122 | set(FLEX_INCLUDE_DIRS ${FLEX_INCLUDE_DIR}) |
| 123 | set(FLEX_LIBRARIES ${FL_LIBRARY}) |
| 124 | |
| 125 | if(FLEX_EXECUTABLE) |
| 126 | |
| 127 | execute_process(COMMAND ${FLEX_EXECUTABLE} --version |
| 128 | OUTPUT_VARIABLE FLEX_version_output |
| 129 | ERROR_VARIABLE FLEX_version_error |
| 130 | RESULT_VARIABLE FLEX_version_result |
| 131 | OUTPUT_STRIP_TRAILING_WHITESPACE) |
| 132 | if(NOT ${FLEX_version_result} EQUAL 0) |
| 133 | if(FLEX_FIND_REQUIRED) |
| 134 | message(SEND_ERROR "Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_output}\n${FLEX_version_error}") |
| 135 | else() |
| 136 | message("Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_output}\n${FLEX_version_error}\nFLEX_VERSION will not be available") |
| 137 | endif() |
| 138 | else() |
| 139 | # older versions of flex printed "/full/path/to/executable version X.Y" |
| 140 | # newer versions use "basename(executable) X.Y" |
| 141 | get_filename_component(FLEX_EXE_NAME_WE "${FLEX_EXECUTABLE}" NAME_WE) |
| 142 | get_filename_component(FLEX_EXE_EXT "${FLEX_EXECUTABLE}" EXT) |
| 143 | string(REGEX REPLACE "^.*${FLEX_EXE_NAME_WE}(${FLEX_EXE_EXT})?\"? (version )?([0-9]+[^ ]*)( .*)?$" "\\3" |
| 144 | FLEX_VERSION "${FLEX_version_output}") |
| 145 | unset(FLEX_EXE_EXT) |
| 146 | unset(FLEX_EXE_NAME_WE) |
| 147 | endif() |
| 148 | |
| 149 | #============================================================ |
| 150 | # FLEX_TARGET (public macro) |
| 151 | #============================================================ |
| 152 | # |
| 153 | macro(FLEX_TARGET Name Input Output) |
| 154 | |
| 155 | set(FLEX_TARGET_PARAM_OPTIONS) |
| 156 | set(FLEX_TARGET_PARAM_ONE_VALUE_KEYWORDS |
| 157 | COMPILE_FLAGS |
| 158 | DEFINES_FILE |
| 159 | ) |
| 160 | set(FLEX_TARGET_PARAM_MULTI_VALUE_KEYWORDS) |
| 161 | |
| 162 | cmake_parse_arguments( |
| 163 | FLEX_TARGET_ARG |
| 164 | "${FLEX_TARGET_PARAM_OPTIONS}" |
| 165 | "${FLEX_TARGET_PARAM_ONE_VALUE_KEYWORDS}" |
| 166 | "${FLEX_TARGET_MULTI_VALUE_KEYWORDS}" |
| 167 | ${ARGN} |
| 168 | ) |
| 169 | |
| 170 | set(FLEX_TARGET_usage "FLEX_TARGET(<Name> <Input> <Output> [COMPILE_FLAGS <string>] [DEFINES_FILE <string>]") |
| 171 | |
| 172 | if(NOT "${FLEX_TARGET_ARG_UNPARSED_ARGUMENTS}" STREQUAL "") |
| 173 | message(SEND_ERROR ${FLEX_TARGET_usage}) |
| 174 | else() |
| 175 | |
| 176 | cmake_policy(GET CMP0098 _flex_CMP0098 |
| 177 | PARENT_SCOPE # undocumented, do not use outside of CMake |
| 178 | ) |
| 179 | set(_flex_INPUT "${Input}") |
| 180 | if("x${_flex_CMP0098}x" STREQUAL "xNEWx") |
| 181 | set(_flex_WORKING_DIR "${CMAKE_CURRENT_BINARY_DIR}") |
| 182 | if(NOT IS_ABSOLUTE "${_flex_INPUT}") |
| 183 | set(_flex_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/${_flex_INPUT}") |
| 184 | endif() |
| 185 | else() |
| 186 | set(_flex_WORKING_DIR "${CMAKE_CURRENT_SOURCE_DIR}") |
| 187 | endif() |
| 188 | unset(_flex_CMP0098) |
| 189 | |
| 190 | set(_flex_OUTPUT "${Output}") |
| 191 | if(NOT IS_ABSOLUTE ${_flex_OUTPUT}) |
| 192 | set(_flex_OUTPUT "${_flex_WORKING_DIR}/${_flex_OUTPUT}") |
| 193 | endif() |
| 194 | set(_flex_TARGET_OUTPUTS "${_flex_OUTPUT}") |
| 195 | |
| 196 | set(_flex_EXE_OPTS "") |
| 197 | if(NOT "${FLEX_TARGET_ARG_COMPILE_FLAGS}" STREQUAL "") |
| 198 | set(_flex_EXE_OPTS "${FLEX_TARGET_ARG_COMPILE_FLAGS}") |
| 199 | separate_arguments(_flex_EXE_OPTS) |
| 200 | endif() |
| 201 | |
| 202 | set(_flex_OUTPUT_HEADER "") |
| 203 | if(NOT "${FLEX_TARGET_ARG_DEFINES_FILE}" STREQUAL "") |
| 204 | set(_flex_OUTPUT_HEADER "${FLEX_TARGET_ARG_DEFINES_FILE}") |
| 205 | if(IS_ABSOLUTE "${_flex_OUTPUT_HEADER}") |
| 206 | set(_flex_OUTPUT_HEADER_ABS "${_flex_OUTPUT_HEADER}") |
| 207 | else() |
| 208 | set(_flex_OUTPUT_HEADER_ABS "${_flex_WORKING_DIR}/${_flex_OUTPUT_HEADER}") |
| 209 | endif() |
| 210 | list(APPEND _flex_TARGET_OUTPUTS "${_flex_OUTPUT_HEADER_ABS}") |
| 211 | list(APPEND _flex_EXE_OPTS --header-file=${_flex_OUTPUT_HEADER_ABS}) |
| 212 | endif() |
| 213 | |
| 214 | get_filename_component(_flex_EXE_NAME_WE "${FLEX_EXECUTABLE}" NAME_WE) |
| 215 | add_custom_command(OUTPUT ${_flex_TARGET_OUTPUTS} |
| 216 | COMMAND ${FLEX_EXECUTABLE} ${_flex_EXE_OPTS} -o${_flex_OUTPUT} ${_flex_INPUT} |
| 217 | VERBATIM |
| 218 | DEPENDS ${_flex_INPUT} |
| 219 | COMMENT "[FLEX][${Name}] Building scanner with ${_flex_EXE_NAME_WE} ${FLEX_VERSION}" |
| 220 | WORKING_DIRECTORY ${_flex_WORKING_DIR}) |
| 221 | |
| 222 | set(FLEX_${Name}_DEFINED TRUE) |
| 223 | set(FLEX_${Name}_OUTPUTS ${_flex_TARGET_OUTPUTS}) |
| 224 | set(FLEX_${Name}_INPUT ${_flex_INPUT}) |
| 225 | set(FLEX_${Name}_COMPILE_FLAGS ${_flex_EXE_OPTS}) |
| 226 | set(FLEX_${Name}_OUTPUT_HEADER ${_flex_OUTPUT_HEADER}) |
| 227 | |
| 228 | unset(_flex_EXE_NAME_WE) |
| 229 | unset(_flex_EXE_OPTS) |
| 230 | unset(_flex_INPUT) |
| 231 | unset(_flex_OUTPUT) |
| 232 | unset(_flex_OUTPUT_HEADER) |
| 233 | unset(_flex_OUTPUT_HEADER_ABS) |
| 234 | unset(_flex_TARGET_OUTPUTS) |
| 235 | unset(_flex_WORKING_DIR) |
| 236 | |
| 237 | endif() |
| 238 | endmacro() |
| 239 | #============================================================ |
| 240 | |
| 241 | |
| 242 | #============================================================ |
| 243 | # ADD_FLEX_BISON_DEPENDENCY (public macro) |
| 244 | #============================================================ |
| 245 | # |
| 246 | macro(ADD_FLEX_BISON_DEPENDENCY FlexTarget BisonTarget) |
| 247 | |
| 248 | if(NOT FLEX_${FlexTarget}_OUTPUTS) |
| 249 | message(SEND_ERROR "Flex target `${FlexTarget}' does not exist.") |
| 250 | endif() |
| 251 | |
| 252 | if(NOT BISON_${BisonTarget}_OUTPUT_HEADER) |
| 253 | message(SEND_ERROR "Bison target `${BisonTarget}' does not exist.") |
| 254 | endif() |
| 255 | |
| 256 | set_source_files_properties(${FLEX_${FlexTarget}_OUTPUTS} |
| 257 | PROPERTIES OBJECT_DEPENDS ${BISON_${BisonTarget}_OUTPUT_HEADER}) |
| 258 | endmacro() |
| 259 | #============================================================ |
| 260 | |
| 261 | endif() |
| 262 | |
| 263 | include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) |
| 264 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(FLEX REQUIRED_VARS FLEX_EXECUTABLE |
| 265 | VERSION_VAR FLEX_VERSION) |