| # 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 is 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) |