| # Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| # file LICENSE.rst or https://cmake.org/licensing for details. | |
| #[=======================================================================[.rst: | |
| FindFLEX | |
| -------- | |
| Finds the Fast Lexical Analyzer (Flex) command-line generator and its library, | |
| and provides CMake commands to create custom build rules for using Flex: | |
| .. code-block:: cmake | |
| find_package(FLEX [<version>] [...]) | |
| Flex generates lexical analyzers, also known as *scanners* or *lexers*. It also | |
| includes a runtime library (``fl``) that supplies support functions for the | |
| generated scanners, such as input handling, buffer management, and error | |
| reporting. | |
| Result Variables | |
| ^^^^^^^^^^^^^^^^ | |
| This module defines the following variables: | |
| ``FLEX_FOUND`` | |
| Boolean indicating whether (the requested version of) Flex was found. | |
| ``FLEX_VERSION`` | |
| The version of Flex found. | |
| ``FLEX_INCLUDE_DIRS`` | |
| The include directories containing headers for using Flex library. | |
| ``FLEX_LIBRARIES`` | |
| The libraries needed to link against to use Flex library. | |
| Cache Variables | |
| ^^^^^^^^^^^^^^^ | |
| The following cache variables may also be set: | |
| ``FLEX_EXECUTABLE`` | |
| The path to the ``flex`` executable. | |
| Commands | |
| ^^^^^^^^ | |
| This module provides the following commands if ``flex`` is found: | |
| Generating Scanners | |
| """"""""""""""""""" | |
| .. command:: flex_target | |
| Creates a custom build rule to generate a scanner file from a lex file using | |
| Flex: | |
| .. code-block:: cmake | |
| flex_target( | |
| <name> | |
| <input-lex-file> | |
| <output-scanner-file> | |
| [DEFINES_FILE <header>] | |
| [OPTIONS <options>...] | |
| [COMPILE_FLAGS <string>] # Deprecated | |
| ) | |
| .. versionchanged:: 3.17 | |
| When policy :policy:`CMP0098` is set to ``NEW``, ``flex`` runs in the | |
| :variable:`CMAKE_CURRENT_BINARY_DIR` directory. | |
| ``<name>`` | |
| String used as an identifier for this command invocation. | |
| ``<input-lex-file>`` | |
| The path to an input Flex source file (``.l``). If given as a relative | |
| path, it will be interpreted relative to the current source directory | |
| (:variable:`CMAKE_CURRENT_SOURCE_DIR`). | |
| ``<output-scanner-file>`` | |
| The path of the output file to be generated by Flex. If given as a relative | |
| path, it will be interpreted relative to the current Flex working directory. | |
| ``DEFINES_FILE <header>`` | |
| .. versionadded:: 3.5 | |
| If Flex is configured to output a header file, this option may be used to | |
| specify its name. If given as a relative path, it will be interpreted | |
| relative to the current Flex working directory. | |
| ``OPTIONS <options>...`` | |
| .. versionadded:: 4.0 | |
| A :ref:`semicolon-separated list <CMake Language Lists>` of extra options | |
| added to the ``flex`` command line. | |
| ``COMPILE_FLAGS <string>`` | |
| .. deprecated:: 4.0 | |
| Superseded by ``OPTIONS <options>...``. | |
| A string of space-separated extra options added to the ``flex`` command | |
| line. A :ref:`semicolon-separated list <CMake Language Lists>` will not | |
| work. | |
| .. rubric:: Command variables | |
| This command also defines the following variables: | |
| ``FLEX_<name>_DEFINED`` | |
| Boolean indicating whether this command was successfully invoked. | |
| ``FLEX_<name>_INPUT`` | |
| The Flex source file, an alias for ``<input-lex-file>``. | |
| ``FLEX_<name>_OUTPUT_HEADER`` | |
| .. versionadded:: 3.5 | |
| The header file generated by ``flex``, if any. | |
| ``FLEX_<name>_OUTPUTS`` | |
| A list of files generated by ``flex``, including the output scanner file, | |
| and the header file. | |
| ``FLEX_<name>_OPTIONS`` | |
| .. versionadded:: 4.0 | |
| A list of command-line options used for the ``flex`` command. | |
| Adding Dependency Between Scanner and Parser | |
| """""""""""""""""""""""""""""""""""""""""""" | |
| .. command:: add_flex_bison_dependency | |
| Adds the required dependency between a scanner and a parser: | |
| .. code-block:: cmake | |
| add_flex_bison_dependency(<flex-name> <bison-name>) | |
| Flex scanners often rely on token definitions generated by Bison, meaning the | |
| code produced by Flex depends on the header file created by Bison. | |
| This command adds the required dependency between a scanner and a parser | |
| where ``<flex-name>`` and ``<bison-name>`` are the first parameters of | |
| respectively ``flex_target(<name> ...)`` and | |
| :command:`bison_target(<name> ...)` commands. | |
| Examples | |
| ^^^^^^^^ | |
| Examples: Finding Flex | |
| """""""""""""""""""""" | |
| Finding Flex: | |
| .. code-block:: cmake | |
| find_package(FLEX) | |
| Finding Flex and specifying its minimum required version: | |
| .. code-block:: cmake | |
| find_package(FLEX 2.5.13) | |
| Finding Flex and making it required (if Flex is not found, processing stops | |
| with an error message): | |
| .. code-block:: cmake | |
| find_package(FLEX 2.5.13 REQUIRED) | |
| Example: Generating Scanner | |
| """"""""""""""""""""""""""" | |
| Finding Flex and generating scanner source file in the current binary directory | |
| from the lex source file in the current source directory: | |
| .. code-block:: cmake | |
| find_package(FLEX) | |
| if(FLEX_FOUND) | |
| flex_target(MyScanner lexer.l lexer.cpp) | |
| endif() | |
| add_executable(foo foo.cc ${FLEX_MyScanner_OUTPUTS}) | |
| Example: Command-line Options | |
| """"""""""""""""""""""""""""" | |
| Adding additional command-line options to the ``flex`` executable can be passed | |
| as a list. For example, adding the ``--warn`` option to report warnings, and | |
| the ``--noline`` (``-L``) to not generate ``#line`` directives. | |
| .. code-block:: cmake | |
| find_package(FLEX) | |
| if(FLEX_FOUND) | |
| flex_target(MyScanner lexer.l lexer.cpp OPTIONS --warn --noline) | |
| endif() | |
| :manual:`Generator expressions <cmake-generator-expressions(7)>` can be used in | |
| the ``OPTIONS <options>...`` argument. For example, to add the ``--debug`` | |
| (``-d``) option only for the ``Debug`` build type: | |
| .. code-block:: cmake | |
| find_package(FLEX) | |
| if(FLEX_FOUND) | |
| flex_target(MyScanner lexer.l lexer.cpp OPTIONS $<$<CONFIG:Debug>:--debug>) | |
| endif() | |
| Example: Using Flex Library | |
| """"""""""""""""""""""""""" | |
| Finding Flex and creating an interface :ref:`imported target <Imported Targets>` | |
| that encapsulates its library usage requirements for linking to a project | |
| target: | |
| .. code-block:: cmake | |
| find_package(FLEX) | |
| if(FLEX_FOUND AND NOT TARGET FLEX::fl) | |
| add_library(FLEX::fl INTERFACE IMPORTED) | |
| set_target_properties( | |
| FLEX::fl | |
| PROPERTIES | |
| INTERFACE_INCLUDE_DIRECTORIES "${FLEX_INCLUDE_DIRS}" | |
| INTERFACE_LINK_LIBRARIES "${FLEX_LIBRARIES}" | |
| ) | |
| endif() | |
| if(FLEX_FOUND) | |
| flex_target(MyScanner lexer.l lexer.cpp) | |
| endif() | |
| add_executable(Foo foo.cc ${FLEX_MyScanner_OUTPUTS}) | |
| target_link_libraries(Foo PRIVATE FLEX::fl) | |
| Example: Using Flex and Bison | |
| """"""""""""""""""""""""""""" | |
| The following example demonstrates, how to use Flex and :module:`Bison | |
| <FindBISON>` in CMake: | |
| .. code-block:: cmake | |
| find_package(BISON) | |
| find_package(FLEX) | |
| if(BISON_FOUND AND FLEX_FOUND) | |
| bison_target(MyParser parser.y parser.cpp) | |
| flex_target(MyScanner lexer.l lexer.cpp) | |
| add_flex_bison_dependency(MyScanner MyParser) | |
| endif() | |
| add_executable(Foo foo.cc ${BISON_MyParser_OUTPUTS} ${FLEX_MyScanner_OUTPUTS}) | |
| # ... | |
| See Also | |
| ^^^^^^^^ | |
| * The :module:`FindBISON` module to find Bison parser generator. | |
| #]=======================================================================] | |
| find_program(FLEX_EXECUTABLE NAMES flex win-flex win_flex DOC "path to the flex executable") | |
| mark_as_advanced(FLEX_EXECUTABLE) | |
| find_library(FL_LIBRARY NAMES fl | |
| DOC "Path to the fl library") | |
| find_path(FLEX_INCLUDE_DIR FlexLexer.h | |
| DOC "Path to the flex headers") | |
| mark_as_advanced(FL_LIBRARY FLEX_INCLUDE_DIR) | |
| set(FLEX_INCLUDE_DIRS ${FLEX_INCLUDE_DIR}) | |
| set(FLEX_LIBRARIES ${FL_LIBRARY}) | |
| if(FLEX_EXECUTABLE) | |
| execute_process(COMMAND ${FLEX_EXECUTABLE} --version | |
| OUTPUT_VARIABLE FLEX_version_output | |
| ERROR_VARIABLE FLEX_version_error | |
| RESULT_VARIABLE FLEX_version_result | |
| OUTPUT_STRIP_TRAILING_WHITESPACE) | |
| if(NOT ${FLEX_version_result} EQUAL 0) | |
| if(FLEX_FIND_REQUIRED) | |
| message(SEND_ERROR "Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_output}\n${FLEX_version_error}") | |
| else() | |
| message("Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_output}\n${FLEX_version_error}\nFLEX_VERSION will not be available") | |
| endif() | |
| else() | |
| # older versions of flex printed "/full/path/to/executable version X.Y" | |
| # newer versions use "basename(executable) X.Y" | |
| get_filename_component(FLEX_EXE_NAME_WE "${FLEX_EXECUTABLE}" NAME_WE) | |
| get_filename_component(FLEX_EXE_EXT "${FLEX_EXECUTABLE}" EXT) | |
| string(REGEX REPLACE "^.*${FLEX_EXE_NAME_WE}(${FLEX_EXE_EXT})?\"? (version )?([0-9]+[^ ]*)( .*)?$" "\\3" | |
| FLEX_VERSION "${FLEX_version_output}") | |
| unset(FLEX_EXE_EXT) | |
| unset(FLEX_EXE_NAME_WE) | |
| endif() | |
| #============================================================ | |
| # flex_target() public macro | |
| #============================================================ | |
| # | |
| macro(FLEX_TARGET Name Input Output) | |
| set(FLEX_TARGET_PARAM_OPTIONS) | |
| set(FLEX_TARGET_PARAM_ONE_VALUE_KEYWORDS | |
| COMPILE_FLAGS | |
| DEFINES_FILE | |
| ) | |
| set(FLEX_TARGET_PARAM_MULTI_VALUE_KEYWORDS OPTIONS) | |
| cmake_parse_arguments( | |
| FLEX_TARGET_ARG | |
| "${FLEX_TARGET_PARAM_OPTIONS}" | |
| "${FLEX_TARGET_PARAM_ONE_VALUE_KEYWORDS}" | |
| "${FLEX_TARGET_PARAM_MULTI_VALUE_KEYWORDS}" | |
| ${ARGN} | |
| ) | |
| string( | |
| JOIN "\n" FLEX_TARGET_usage | |
| "Usage:" | |
| " flex_target(" | |
| " <name>" | |
| " <input-lex-file>" | |
| " <output-scanner-file>" | |
| " [DEFINES_FILE <header>]" | |
| " [OPTIONS <options>...]" | |
| " [COMPILE_FLAGS <string>]" | |
| " )" | |
| ) | |
| if(NOT "${FLEX_TARGET_ARG_UNPARSED_ARGUMENTS}" STREQUAL "") | |
| message( | |
| SEND_ERROR | |
| "Unrecognized arguments: ${FLEX_TARGET_ARG_UNPARSED_ARGUMENTS}\n" | |
| "${FLEX_TARGET_usage}" | |
| ) | |
| else() | |
| cmake_policy(GET CMP0098 _flex_CMP0098 | |
| PARENT_SCOPE # undocumented, do not use outside of CMake | |
| ) | |
| set(_flex_INPUT "${Input}") | |
| if("x${_flex_CMP0098}x" STREQUAL "xNEWx") | |
| set(_flex_WORKING_DIR "${CMAKE_CURRENT_BINARY_DIR}") | |
| if(NOT IS_ABSOLUTE "${_flex_INPUT}") | |
| set(_flex_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/${_flex_INPUT}") | |
| endif() | |
| else() | |
| set(_flex_WORKING_DIR "${CMAKE_CURRENT_SOURCE_DIR}") | |
| endif() | |
| unset(_flex_CMP0098) | |
| set(_flex_OUTPUT "${Output}") | |
| if(NOT IS_ABSOLUTE ${_flex_OUTPUT}) | |
| set(_flex_OUTPUT "${_flex_WORKING_DIR}/${_flex_OUTPUT}") | |
| endif() | |
| set(_flex_TARGET_OUTPUTS "${_flex_OUTPUT}") | |
| set(_flex_EXE_OPTS "") | |
| if(NOT "${FLEX_TARGET_ARG_COMPILE_FLAGS}" STREQUAL "") | |
| set(_flex_EXE_OPTS "${FLEX_TARGET_ARG_COMPILE_FLAGS}") | |
| separate_arguments(_flex_EXE_OPTS) | |
| endif() | |
| if(FLEX_TARGET_ARG_OPTIONS) | |
| list(APPEND _flex_EXE_OPTS ${FLEX_TARGET_ARG_OPTIONS}) | |
| endif() | |
| set(_flex_OUTPUT_HEADER "") | |
| if(NOT "${FLEX_TARGET_ARG_DEFINES_FILE}" STREQUAL "") | |
| set(_flex_OUTPUT_HEADER "${FLEX_TARGET_ARG_DEFINES_FILE}") | |
| if(IS_ABSOLUTE "${_flex_OUTPUT_HEADER}") | |
| set(_flex_OUTPUT_HEADER_ABS "${_flex_OUTPUT_HEADER}") | |
| else() | |
| set(_flex_OUTPUT_HEADER_ABS "${_flex_WORKING_DIR}/${_flex_OUTPUT_HEADER}") | |
| endif() | |
| list(APPEND _flex_TARGET_OUTPUTS "${_flex_OUTPUT_HEADER_ABS}") | |
| list(APPEND _flex_EXE_OPTS --header-file=${_flex_OUTPUT_HEADER_ABS}) | |
| endif() | |
| # Flex cannot create output directories. Create any missing determined | |
| # directories where the files will be generated if they don't exist yet. | |
| set(_flex_MAKE_DIRECTORY_COMMAND "") | |
| foreach(output IN LISTS _flex_TARGET_OUTPUTS) | |
| cmake_path(GET output PARENT_PATH dir) | |
| if(dir) | |
| list(APPEND _flex_MAKE_DIRECTORY_COMMAND ${dir}) | |
| endif() | |
| unset(dir) | |
| endforeach() | |
| if(_flex_MAKE_DIRECTORY_COMMAND) | |
| list(REMOVE_DUPLICATES _flex_MAKE_DIRECTORY_COMMAND) | |
| list( | |
| PREPEND | |
| _flex_MAKE_DIRECTORY_COMMAND | |
| COMMAND ${CMAKE_COMMAND} -E make_directory | |
| ) | |
| endif() | |
| get_filename_component(_flex_EXE_NAME_WE "${FLEX_EXECUTABLE}" NAME_WE) | |
| add_custom_command(OUTPUT ${_flex_TARGET_OUTPUTS} | |
| ${_flex_MAKE_DIRECTORY_COMMAND} | |
| COMMAND ${FLEX_EXECUTABLE} ${_flex_EXE_OPTS} -o${_flex_OUTPUT} ${_flex_INPUT} | |
| VERBATIM | |
| DEPENDS ${_flex_INPUT} | |
| COMMENT "[FLEX][${Name}] Building scanner with ${_flex_EXE_NAME_WE} ${FLEX_VERSION}" | |
| WORKING_DIRECTORY ${_flex_WORKING_DIR} | |
| COMMAND_EXPAND_LISTS) | |
| set(FLEX_${Name}_DEFINED TRUE) | |
| set(FLEX_${Name}_OUTPUTS ${_flex_TARGET_OUTPUTS}) | |
| set(FLEX_${Name}_INPUT ${_flex_INPUT}) | |
| set(FLEX_${Name}_OPTIONS ${_flex_EXE_OPTS}) | |
| set(FLEX_${Name}_COMPILE_FLAGS ${_flex_EXE_OPTS}) | |
| set(FLEX_${Name}_OUTPUT_HEADER ${_flex_OUTPUT_HEADER}) | |
| unset(_flex_EXE_NAME_WE) | |
| unset(_flex_EXE_OPTS) | |
| unset(_flex_INPUT) | |
| unset(_flex_MAKE_DIRECTORY_COMMAND) | |
| unset(_flex_OUTPUT) | |
| unset(_flex_OUTPUT_HEADER) | |
| unset(_flex_OUTPUT_HEADER_ABS) | |
| unset(_flex_TARGET_OUTPUTS) | |
| unset(_flex_WORKING_DIR) | |
| endif() | |
| endmacro() | |
| #============================================================ | |
| #============================================================ | |
| # add_flex_bison_dependency() public macro | |
| #============================================================ | |
| # | |
| macro(ADD_FLEX_BISON_DEPENDENCY FlexTarget BisonTarget) | |
| if(NOT FLEX_${FlexTarget}_OUTPUTS) | |
| message(SEND_ERROR "Flex target `${FlexTarget}' does not exist.") | |
| endif() | |
| if(NOT BISON_${BisonTarget}_OUTPUT_HEADER) | |
| message(SEND_ERROR "Bison target `${BisonTarget}' does not exist.") | |
| endif() | |
| set_source_files_properties(${FLEX_${FlexTarget}_OUTPUTS} | |
| PROPERTIES OBJECT_DEPENDS ${BISON_${BisonTarget}_OUTPUT_HEADER}) | |
| endmacro() | |
| #============================================================ | |
| endif() | |
| include(FindPackageHandleStandardArgs) | |
| find_package_handle_standard_args(FLEX REQUIRED_VARS FLEX_EXECUTABLE | |
| VERSION_VAR FLEX_VERSION) |