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:
5FindFLEX
6--------
7
8Find Fast Lexical Analyzer (Flex) executable and provides a macro
9to generate custom build rules
10
11
12
13The 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
25The minimum required version of flex can be specified using the
26standard syntax, e.g.  :command:`find_package(FLEX 2.5.13)`
27
28
29
30If 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
39which creates a custom command to generate the ``FlexOutput`` file from
40the ``FlexInput`` file.  Name is an alias used to get details of this custom
41command.  If ``COMPILE_FLAGS`` option is specified, the next
42parameter 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
53The 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
65Flex scanners often use tokens defined by Bison: the code generated
66by Flex depends of the header generated by Bison.  This module also
67defines a macro:
68
69::
70
71  ADD_FLEX_BISON_DEPENDENCY(FlexTarget BisonTarget)
72
73which adds the required dependency between a scanner and a parser
74where ``FlexTarget`` and ``BisonTarget`` are the first parameters of
75respectively ``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
111find_program(FLEX_EXECUTABLE NAMES flex win-flex win_flex DOC "path to the flex executable")
112mark_as_advanced(FLEX_EXECUTABLE)
113
114find_library(FL_LIBRARY NAMES fl
115  DOC "Path to the fl library")
116
117find_path(FLEX_INCLUDE_DIR FlexLexer.h
118  DOC "Path to the flex headers")
119
120mark_as_advanced(FL_LIBRARY FLEX_INCLUDE_DIR)
121
122set(FLEX_INCLUDE_DIRS ${FLEX_INCLUDE_DIR})
123set(FLEX_LIBRARIES ${FL_LIBRARY})
124
125if(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
261endif()
262
263include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
264FIND_PACKAGE_HANDLE_STANDARD_ARGS(FLEX REQUIRED_VARS FLEX_EXECUTABLE
265                                       VERSION_VAR FLEX_VERSION)
266