Skip to content

Commit 5c144d1

Browse files
committed
Add lexer and parser generator script
This improves usability to simply call: cmake -P cmake/scripts/GenerateLexersParsers.cmake instead of doing the entire CMake configuration phase and then executing some target to generate files.
1 parent 6d46d9b commit 5c144d1

File tree

12 files changed

+342
-71
lines changed

12 files changed

+342
-71
lines changed

cmake/Zend/CMakeLists.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -536,9 +536,6 @@ if(BISON_FOUND)
536536
DEFINES_FILE ${CMAKE_CURRENT_SOURCE_DIR}/zend_ini_parser.h
537537
)
538538

539-
add_custom_target(zend_ini_parser DEPENDS ${BISON_TARGET_outputs})
540-
add_dependencies(php_generate_files zend_ini_parser)
541-
542539
bison_target(
543540
zend_language_parser
544541
zend_language_parser.y
@@ -605,7 +602,6 @@ if(BISON_FOUND)
605602
)
606603

607604
add_dependencies(zend zend_patch_language_parser)
608-
add_dependencies(php_generate_files zend_patch_language_parser)
609605
endif()
610606

611607
if(RE2C_FOUND)
@@ -615,6 +611,7 @@ if(RE2C_FOUND)
615611
${CMAKE_CURRENT_SOURCE_DIR}/zend_language_scanner.c
616612
HEADER ${CMAKE_CURRENT_SOURCE_DIR}/zend_language_scanner_defs.h
617613
OPTIONS --case-inverted -cbdF
614+
CODEGEN
618615
)
619616

620617
re2c_target(
@@ -623,6 +620,7 @@ if(RE2C_FOUND)
623620
${CMAKE_CURRENT_SOURCE_DIR}/zend_ini_scanner.c
624621
HEADER ${CMAKE_CURRENT_SOURCE_DIR}/zend_ini_scanner_defs.h
625622
OPTIONS --case-inverted -cbdF
623+
CODEGEN
626624
)
627625
endif()
628626

cmake/cmake/Bootstrap.cmake

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,6 @@ add_library(php_sapi INTERFACE)
4848
add_library(PHP::sapi ALIAS php_sapi)
4949
target_link_libraries(php_sapi INTERFACE PHP::config)
5050

51-
# Create a custom target for generating files (parsers, lexers, etc.) manually:
52-
# cmake --build <dir> -t php_generate_files
53-
add_custom_target(php_generate_files)
54-
5551
# Configure build types.
5652
include(cmake/BuildTypes.cmake)
5753

cmake/cmake/Configuration.cmake

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,6 @@ set(PHP_ZLIB_MIN_VERSION 1.2.0.4)
213213
set(PHP_BZIP2_MIN_VERSION 1.0.0)
214214

215215
# Additional metadata for external packages to avoid duplication.
216-
set_package_properties(
217-
BISON
218-
PROPERTIES
219-
URL "https://www.gnu.org/software/bison/"
220-
DESCRIPTION "General-purpose parser generator"
221-
)
222-
223216
set_package_properties(
224217
BZip2
225218
PROPERTIES

cmake/cmake/Requirements.cmake

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,6 @@ if(
112112
TYPE REQUIRED
113113
PURPOSE "Necessary to generate PHP lexer files."
114114
)
115-
116-
add_dependencies(php_generate_files re2c_generate_files)
117115
endif()
118116

119117
################################################################################
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#[=============================================================================[
2+
# FindBISON
3+
4+
Find the bison utility.
5+
6+
See: https://cmake.org/cmake/help/latest/module/FindBISON.html
7+
8+
This module overrides the upstream CMake `FindBISON` module with few
9+
customizations.
10+
11+
A new `bison_generate()` function is added to be able to use it in command-line
12+
scripts.
13+
14+
```cmake
15+
bison_generate(
16+
<name>
17+
<input>
18+
<output>
19+
[COMPILE_OPTIONS <flags>]
20+
[DEFINES_FILE <file>]
21+
[VERBOSE [REPORT_FILE <file>]]
22+
)
23+
```
24+
#]=============================================================================]
25+
26+
include(FeatureSummary)
27+
28+
set_package_properties(
29+
BISON
30+
PROPERTIES
31+
URL "https://www.gnu.org/software/bison/"
32+
DESCRIPTION "General-purpose parser generator"
33+
)
34+
35+
# Find package with upstream CMake module; override CMAKE_MODULE_PATH to prevent
36+
# the maximum nesting/recursion depth error on some systems, like macOS.
37+
set(_php_cmake_module_path ${CMAKE_MODULE_PATH})
38+
unset(CMAKE_MODULE_PATH)
39+
include(FindBISON)
40+
set(CMAKE_MODULE_PATH ${_php_cmake_module_path})
41+
unset(_php_cmake_module_path)
42+
43+
if(NOT BISON_FOUND)
44+
return()
45+
endif()
46+
47+
function(bison_generate)
48+
cmake_parse_arguments(
49+
PARSE_ARGV
50+
3
51+
parsed # prefix
52+
"VERBOSE" # options
53+
"DEFINES_FILE;REPORT_FILE" # one-value keywords
54+
"COMPILE_OPTIONS" # multi-value keywords
55+
)
56+
57+
if(parsed_UNPARSED_ARGUMENTS)
58+
message(FATAL_ERROR "Bad arguments: ${parsed_UNPARSED_ARGUMENTS}")
59+
endif()
60+
61+
if(parsed_KEYWORDS_MISSING_VALUES)
62+
message(FATAL_ERROR "Missing values for: ${parsed_KEYWORDS_MISSING_VALUES}")
63+
endif()
64+
65+
set(input ${ARGV1})
66+
if(NOT IS_ABSOLUTE "${input}")
67+
set(input ${CMAKE_CURRENT_SOURCE_DIR}/${input})
68+
endif()
69+
70+
set(output ${ARGV2})
71+
if(NOT IS_ABSOLUTE "${output}")
72+
set(output ${CMAKE_CURRENT_BINARY_DIR}/${output})
73+
endif()
74+
75+
set(options ${parsed_OPTIONS})
76+
77+
message(
78+
STATUS
79+
"[BISON][${ARGV0}] Generating parser with bison ${BISON_VERSION}"
80+
)
81+
82+
execute_process(COMMAND ${BISON_EXECUTABLE} ${options} -o ${output} ${input})
83+
endfunction()

cmake/cmake/modules/FindRE2C.cmake

Lines changed: 110 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,9 @@ syntax, e.g. 'find_package(RE2C 0.15.3)'.
1414
## Cache variables
1515
1616
* `RE2C_EXECUTABLE` - Path to the re2c program. When RE2C is downloaded and
17-
built from source as part of the built (using below ExternalProject), this
18-
path will not exist until the built phase.
19-
20-
Custom target:
21-
22-
* `re2c_generate_files` - A custom target for generating lexer files:
23-
24-
```sh
25-
cmake --build <dir> -t re2c_generate_files
26-
```
27-
28-
or to add it as a dependency to other targets:
29-
30-
```cmake
31-
add_dependencies(some_target re2c_generate_files)
32-
```
17+
built from source as part of the built (using the `ExternalProject` CMake
18+
module), this path will be autofilled to the built re2c and will not exist
19+
until the build phase.
3320
3421
## Hints
3522
@@ -61,22 +48,82 @@ re2c_target(
6148
[DEPENDS <depends>...]
6249
[NO_DEFAULT_OPTIONS]
6350
[NO_COMPUTED_GOTOS]
51+
[CODEGEN]
6452
)
6553
```
6654
67-
* `<name>` - Target name.
68-
* `<input>` - The re2c template file input. Relative source file path is
69-
interpreted as being relative to the current source directory.
70-
* `<output>` - The output file. Relative output file path is interpreted as
71-
being relative to the current binary directory.
72-
* `HEADER` - Generate a <header> file. Relative header file path is interpreted
73-
as being relative to the current binary directory.
74-
* `OPTIONS` - List of additional options to pass to re2c command-line tool.
75-
* `DEPENDS` - Optional list of dependent files to regenerate the output file.
55+
This will add a custom command and a custom target `<name>` that generates lexer
56+
file `<output>` from the given `<input>` re2c template file using the re2c
57+
utility. Relative source file path `<input> is interpreted as being relative to
58+
the current source directory. Relative `<output>` file path is interpreted as
59+
being relative to the current binary directory.
60+
61+
### Options
62+
63+
* `HEADER <header>` - Generate a given `<header>` file. Relative header file
64+
path is interpreted as being relative to the current binary directory.
65+
66+
* `OPTIONS <options>...` - List of additional options to pass to re2c
67+
command-line tool.
68+
69+
* `DEPENDS <depends>...` - Optional list of dependent files to regenerate the
70+
output file.
71+
7672
* `NO_DEFAULT_OPTIONS` - If specified, then the options from
7773
`RE2C_DEFAULT_OPTIONS` are not passed to the re2c invocation.
74+
7875
* `NO_COMPUTED_GOTOS` - If specified when using the `RE2C_USE_COMPUTED_GOTOS`,
7976
then the computed gotos option is not passed to the re2c invocation.
77+
78+
* `CODEGEN` - adds the `CODEGEN` option to the re2c's `add_custom_command()`
79+
call. Works as of CMake 3.31 when policy `CMP0171` is set to `NEW`, which
80+
provides a global CMake `codegen` target for convenience to call only the
81+
code-generation-related targets and skips the majority of the build:
82+
83+
```sh
84+
cmake --build <dir> --target codegen
85+
```
86+
87+
## Examples
88+
89+
The `re2c_target()` also creates a custom target called `<name>` that can be
90+
used in more complex scenarios, like defining dependencies to other targets:
91+
92+
```cmake
93+
# CMakeLists.txt
94+
95+
find_package(RE2C)
96+
97+
if(RE2C_FOUND)
98+
re2c_target(foo_lexer lexer.re lexer.c)
99+
add_dependencies(some_target foo_lexer)
100+
endif()
101+
```
102+
103+
Or to run only the specific `foo_lexer` target, which generates the lexer.
104+
105+
```sh
106+
cmake --build <dir> --target foo_lexer
107+
```
108+
109+
When running in script mode:
110+
111+
```sh
112+
cmake -P script.cmake
113+
```
114+
115+
The generated file is created right away for convenience and custom target is
116+
not created:
117+
118+
```cmake
119+
# script.cmake
120+
121+
find_package(RE2C)
122+
123+
if(RE2C_FOUND)
124+
re2c_target(foo_lexer lexer.re lexer.c)
125+
endif()
126+
```
80127
#]=============================================================================]
81128

82129
include(CheckSourceCompiles)
@@ -91,10 +138,6 @@ set_package_properties(
91138
DESCRIPTION "Free and open-source lexer generator"
92139
)
93140

94-
if(NOT TARGET re2c_generate_files)
95-
add_custom_target(re2c_generate_files)
96-
endif()
97-
98141
find_program(
99142
RE2C_EXECUTABLE
100143
NAMES re2c
@@ -103,12 +146,12 @@ find_program(
103146
mark_as_advanced(RE2C_EXECUTABLE)
104147

105148
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.29)
106-
set(_re2cCondition IS_EXECUTABLE ${RE2C_EXECUTABLE})
149+
set(_re2cTest IS_EXECUTABLE)
107150
else()
108-
set(_re2cCondition EXISTS ${RE2C_EXECUTABLE})
151+
set(_re2cTest EXISTS)
109152
endif()
110153

111-
if(${_re2cCondition})
154+
if(${_re2cTest} ${RE2C_EXECUTABLE})
112155
execute_process(
113156
COMMAND ${RE2C_EXECUTABLE} --vernum
114157
OUTPUT_VARIABLE RE2C_VERSION_NUM
@@ -201,9 +244,9 @@ find_package_handle_standard_args(
201244
REASON_FAILURE_MESSAGE "re2c not found. Please install re2c."
202245
)
203246

204-
unset(_re2cCondition)
205247
unset(_re2cMsg)
206248
unset(_re2cRequiredVars)
249+
unset(_re2cTest)
207250
unset(_re2cVersionValid)
208251

209252
if(NOT RE2C_FOUND)
@@ -241,10 +284,10 @@ function(re2c_target)
241284
cmake_parse_arguments(
242285
PARSE_ARGV
243286
3
244-
parsed # prefix
245-
"NO_DEFAULT_OPTIONS;NO_COMPUTED_GOTOS" # options
246-
"HEADER" # one-value keywords
247-
"OPTIONS;DEPENDS" # multi-value keywords
287+
parsed # prefix
288+
"NO_DEFAULT_OPTIONS;NO_COMPUTED_GOTOS;CODEGEN" # options
289+
"HEADER" # one-value keywords
290+
"OPTIONS;DEPENDS" # multi-value keywords
248291
)
249292

250293
if(parsed_UNPARSED_ARGUMENTS)
@@ -289,31 +332,52 @@ function(re2c_target)
289332

290333
list(APPEND outputs ${header})
291334

292-
# When header option is used before version 1.2, also the '-c' option is
293-
# required. Before 1.1 -c long variant is '--start-conditions' and after 1.1
294-
# '--conditions'.
335+
# When header option is used before re2c version 1.2, also the '-c' option
336+
# is required. Before 1.1 '-c' long variant is '--start-conditions' and
337+
# after 1.1 '--conditions'.
295338
if(RE2C_VERSION VERSION_LESS_EQUAL 1.2)
296339
list(APPEND options -c)
297340
endif()
298341

299-
# Since version 3.0, --header is the new alias option for --type-header.
342+
# Since re2c version 3.0, '--header' is the new alias option for the
343+
# '--type-header' option.
300344
if(RE2C_VERSION VERSION_GREATER_EQUAL 3.0)
301345
list(APPEND options --header ${header})
302346
else()
303347
list(APPEND options --type-header ${header})
304348
endif()
305349
endif()
306350

351+
set(message "[RE2C][${ARGV0}] Generating lexer with re2c ${RE2C_VERSION}")
352+
set(command ${RE2C_EXECUTABLE} ${options} --output ${output} ${input})
353+
354+
if(CMAKE_SCRIPT_MODE_FILE)
355+
message(STATUS "${message}")
356+
execute_process(COMMAND ${command})
357+
return()
358+
endif()
359+
360+
set(codegen "")
361+
if(
362+
parsed_CODEGEN
363+
AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.31
364+
AND POLICY CMP0171
365+
)
366+
cmake_policy(GET CMP0171 cmp0171)
367+
368+
if(cmp0171 STREQUAL "NEW")
369+
set(codegen CODEGEN)
370+
endif()
371+
endif()
372+
307373
add_custom_command(
308374
OUTPUT ${outputs}
309-
COMMAND ${RE2C_EXECUTABLE}
310-
${options}
311-
--output ${output}
312-
${input}
375+
COMMAND ${command}
313376
DEPENDS ${input} ${parsed_DEPENDS} $<TARGET_NAME_IF_EXISTS:RE2C::RE2C>
314-
COMMENT "[RE2C][${ARGV0}] Building lexer with re2c ${RE2C_VERSION}"
377+
COMMENT "${message}"
315378
VERBATIM
316379
COMMAND_EXPAND_LISTS
380+
${codegen}
317381
)
318382

319383
add_custom_target(
@@ -322,6 +386,4 @@ function(re2c_target)
322386
DEPENDS ${outputs}
323387
COMMENT "[RE2C] Building lexer with re2c ${RE2C_VERSION}"
324388
)
325-
326-
add_dependencies(re2c_generate_files ${ARGV0})
327389
endfunction()

0 commit comments

Comments
 (0)