3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-06-06 09:00:54 +00:00

CMake: improve and vendor upstream FindBISON module.

This change uses the `find_program(VALIDATOR)` functionality available
since CMake 3.25 to find the acceptable Bison executable instead of
failing if the first one found is too old. (macOS ships with Bison 2.6.)

This change also explicitly finds a usable M4 and ensures the Bison
command will use it. (macOS ships with an m4 which can fail if XCode
Tools aren't installed.)
This commit is contained in:
Catherine 2026-06-05 15:52:48 +00:00
parent 017c99be5b
commit c9e3ae8c9a

346
cmake/FindBISON.cmake Normal file
View file

@ -0,0 +1,346 @@
# Based on CMake v3.31.0 `Modules/FindBISON.cmake`, changed as follows:
# - Continue searching for Bison if an executable does not match the requested
# version constraint.
# - Search for a usable M4 and run Bison with the `M4` environment variable.
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindBISON
---------
Find ``bison`` executable and provide a macro to generate custom build rules.
The module defines the following variables:
``BISON_EXECUTABLE``
path to the ``bison`` program
``BISON_VERSION``
version of ``bison``
``BISON_FOUND``
"True" if the program was found
The minimum required version of ``bison`` can be specified using the
standard CMake syntax, e.g. :command:`find_package(BISON 2.1.3)`.
If ``bison`` is found, the module defines the macro::
BISON_TARGET(<Name> <YaccInput> <CodeOutput>
[COMPILE_FLAGS <flags>]
[DEFINES_FILE <file>]
[VERBOSE [<file>]]
[REPORT_FILE <file>]
)
which will create a custom rule to generate a parser. ``<YaccInput>`` is
the path to a yacc file. ``<CodeOutput>`` is the name of the source file
generated by bison. A header file is also be generated, and contains
the token list.
.. versionchanged:: 3.14
When :policy:`CMP0088` is set to ``NEW``, ``bison`` runs in the
:variable:`CMAKE_CURRENT_BINARY_DIR` directory.
The options are:
``COMPILE_FLAGS <flags>``
Specify flags to be added to the ``bison`` command line.
``DEFINES_FILE <file>``
.. versionadded:: 3.4
Specify a non-default header ``<file>`` to be generated by ``bison``.
``VERBOSE [<file>]``
Tell ``bison`` to write a report file of the grammar and parser.
.. deprecated:: 3.7
If ``<file>`` is given, it specifies path the report file is copied to.
``[<file>]`` is left for backward compatibility of this module.
Use ``VERBOSE REPORT_FILE <file>``.
``REPORT_FILE <file>``
.. versionadded:: 3.7
Specify a non-default report ``<file>``, if generated.
The macro defines the following variables:
``BISON_<Name>_DEFINED``
``True`` is the macro ran successfully
``BISON_<Name>_INPUT``
The input source file, an alias for <YaccInput>
``BISON_<Name>_OUTPUT_SOURCE``
The source file generated by bison
``BISON_<Name>_OUTPUT_HEADER``
The header file generated by bison
``BISON_<Name>_OUTPUTS``
All files generated by bison including the source, the header and the report
``BISON_<Name>_COMPILE_FLAGS``
Options used in the ``bison`` command line
Example usage:
.. code-block:: cmake
find_package(BISON)
BISON_TARGET(MyParser parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp
DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/parser.h)
add_executable(Foo main.cpp ${BISON_MyParser_OUTPUTS})
#]=======================================================================]
function(BISON_m4_validator result_var executable)
execute_process(COMMAND ${executable} --version
OUTPUT_QUIET
ERROR_QUIET
RESULT_VARIABLE M4_version_result
)
if (NOT M4_version_result EQUAL 0)
set(${result_var} FALSE PARENT_SCOPE)
endif()
endfunction()
find_program(BISON_M4_EXECUTABLE
NAMES m4
VALIDATOR BISON_m4_validator
DOC "path to the m4 executable (used by bison)"
)
function(BISON_get_version result_var executable)
# the bison commands should be executed with the C locale, otherwise
# the message (which are parsed) may be translated
set(_Bison_SAVED_LC_ALL "$ENV{LC_ALL}")
set(ENV{LC_ALL} C)
execute_process(COMMAND ${executable} --version
OUTPUT_VARIABLE BISON_version_output
ERROR_VARIABLE BISON_version_error
RESULT_VARIABLE BISON_version_result
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(ENV{LC_ALL} ${_Bison_SAVED_LC_ALL})
if(NOT ${BISON_version_result} EQUAL 0)
message(SEND_ERROR "Command \"${executable} --version\" failed with output:\n${BISON_version_error}")
else()
# Bison++
if("${BISON_version_output}" MATCHES "^bison\\+\\+ Version ([^,]+)")
set(${result_var} "${CMAKE_MATCH_1}")
# GNU Bison
elseif("${BISON_version_output}" MATCHES "^bison \\(GNU Bison\\) ([^\n]+)\n")
set(${result_var} "${CMAKE_MATCH_1}")
elseif("${BISON_version_output}" MATCHES "^GNU Bison (version )?([^\n]+)")
set(${result_var} "${CMAKE_MATCH_2}")
endif()
endif()
return(PROPAGATE ${result_var})
endfunction()
function(BISON_validator result_var executable)
BISON_get_version(bison_version ${executable})
find_package_check_version("${bison_version}" ${result_var})
return(PROPAGATE ${result_var})
endfunction()
find_program(BISON_EXECUTABLE
NAMES bison win-bison win_bison
VALIDATOR BISON_validator
DOC "path to the bison executable"
)
mark_as_advanced(BISON_EXECUTABLE)
if(BISON_EXECUTABLE)
BISON_get_version(BISON_VERSION ${BISON_EXECUTABLE})
# internal macro
# sets BISON_TARGET_cmdopt
macro(BISON_TARGET_option_extraopts Options)
set(BISON_TARGET_cmdopt "")
set(BISON_TARGET_extraopts "${Options}")
separate_arguments(BISON_TARGET_extraopts)
list(APPEND BISON_TARGET_cmdopt ${BISON_TARGET_extraopts})
endmacro()
# internal macro
# sets BISON_TARGET_output_header and BISON_TARGET_cmdopt
macro(BISON_TARGET_option_defines BisonOutput Header)
if("${Header}" STREQUAL "")
# default header path generated by bison (see option -d)
string(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\2" _fileext "${BisonOutput}")
string(REPLACE "c" "h" _fileext ${_fileext})
string(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\1${_fileext}"
BISON_TARGET_output_header "${BisonOutput}")
list(APPEND BISON_TARGET_cmdopt "-d")
else()
set(BISON_TARGET_output_header "${Header}")
list(APPEND BISON_TARGET_cmdopt "--defines=${BISON_TARGET_output_header}")
endif()
endmacro()
# internal macro
# sets BISON_TARGET_verbose_file and BISON_TARGET_cmdopt
macro(BISON_TARGET_option_report_file BisonOutput ReportFile)
if("${ReportFile}" STREQUAL "")
get_filename_component(BISON_TARGET_output_path "${BisonOutput}" PATH)
get_filename_component(BISON_TARGET_output_name "${BisonOutput}" NAME_WE)
set(BISON_TARGET_verbose_file
"${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output")
else()
set(BISON_TARGET_verbose_file "${ReportFile}")
list(APPEND BISON_TARGET_cmdopt "--report-file=${BISON_TARGET_verbose_file}")
endif()
if(NOT IS_ABSOLUTE "${BISON_TARGET_verbose_file}")
cmake_policy(GET CMP0088 _BISON_CMP0088
PARENT_SCOPE # undocumented, do not use outside of CMake
)
if("x${_BISON_CMP0088}x" STREQUAL "xNEWx")
set(BISON_TARGET_verbose_file "${CMAKE_CURRENT_BINARY_DIR}/${BISON_TARGET_verbose_file}")
else()
set(BISON_TARGET_verbose_file "${CMAKE_CURRENT_SOURCE_DIR}/${BISON_TARGET_verbose_file}")
endif()
unset(_BISON_CMP0088)
endif()
endmacro()
# internal macro
# adds a custom command and sets
# BISON_TARGET_cmdopt, BISON_TARGET_extraoutputs
macro(BISON_TARGET_option_verbose Name BisonOutput filename)
cmake_policy(GET CMP0088 _BISON_CMP0088
PARENT_SCOPE # undocumented, do not use outside of CMake
)
set(_BISON_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
if("x${_BISON_CMP0088}x" STREQUAL "xNEWx")
set(_BISON_WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif()
unset(_BISON_CMP0088)
list(APPEND BISON_TARGET_cmdopt "--verbose")
list(APPEND BISON_TARGET_outputs
"${BISON_TARGET_verbose_file}")
if (NOT "${filename}" STREQUAL "")
if(IS_ABSOLUTE "${filename}")
set(BISON_TARGET_verbose_extra_file "${filename}")
else()
set(BISON_TARGET_verbose_extra_file "${_BISON_WORKING_DIRECTORY}/${filename}")
endif()
add_custom_command(OUTPUT ${BISON_TARGET_verbose_extra_file}
COMMAND ${CMAKE_COMMAND} -E copy
"${BISON_TARGET_verbose_file}"
"${filename}"
VERBATIM
DEPENDS
"${BISON_TARGET_verbose_file}"
COMMENT "[BISON][${Name}] Copying bison verbose table to ${filename}"
WORKING_DIRECTORY ${_BISON_WORKING_DIRECTORY})
list(APPEND BISON_TARGET_extraoutputs
"${BISON_TARGET_verbose_extra_file}")
unset(BISON_TARGET_verbose_extra_file)
unset(_BISON_WORKING_DIRECTORY)
endif()
endmacro()
#============================================================
# BISON_TARGET (public macro)
#============================================================
#
macro(BISON_TARGET Name BisonInput BisonOutput)
set(BISON_TARGET_outputs "${BisonOutput}")
set(BISON_TARGET_extraoutputs "")
# Parsing parameters
set(BISON_TARGET_PARAM_OPTIONS
)
set(BISON_TARGET_PARAM_ONE_VALUE_KEYWORDS
COMPILE_FLAGS
DEFINES_FILE
REPORT_FILE
)
set(BISON_TARGET_PARAM_MULTI_VALUE_KEYWORDS
VERBOSE
)
cmake_parse_arguments(
BISON_TARGET_ARG
"${BISON_TARGET_PARAM_OPTIONS}"
"${BISON_TARGET_PARAM_ONE_VALUE_KEYWORDS}"
"${BISON_TARGET_PARAM_MULTI_VALUE_KEYWORDS}"
${ARGN}
)
if(NOT "${BISON_TARGET_ARG_UNPARSED_ARGUMENTS}" STREQUAL "")
message(SEND_ERROR "Usage")
elseif("${BISON_TARGET_ARG_VERBOSE}" MATCHES ";")
# [VERBOSE [<file>] hack: <file> is non-multi value by usage
message(SEND_ERROR "Usage")
else()
BISON_TARGET_option_extraopts("${BISON_TARGET_ARG_COMPILE_FLAGS}")
BISON_TARGET_option_defines("${BisonOutput}" "${BISON_TARGET_ARG_DEFINES_FILE}")
BISON_TARGET_option_report_file("${BisonOutput}" "${BISON_TARGET_ARG_REPORT_FILE}")
if(NOT "${BISON_TARGET_ARG_VERBOSE}" STREQUAL "")
BISON_TARGET_option_verbose(${Name} ${BisonOutput} "${BISON_TARGET_ARG_VERBOSE}")
else()
# [VERBOSE [<file>]] is used with no argument or is not used
set(BISON_TARGET_args "${ARGN}")
list(FIND BISON_TARGET_args "VERBOSE" BISON_TARGET_args_indexof_verbose)
if(${BISON_TARGET_args_indexof_verbose} GREATER -1)
# VERBOSE is used without <file>
BISON_TARGET_option_verbose(${Name} ${BisonOutput} "")
endif()
endif()
list(APPEND BISON_TARGET_outputs "${BISON_TARGET_output_header}")
cmake_policy(GET CMP0088 _BISON_CMP0088
PARENT_SCOPE # undocumented, do not use outside of CMake
)
set(_BISON_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(_BisonInput "${BisonInput}")
if("x${_BISON_CMP0088}x" STREQUAL "xNEWx")
set(_BISON_WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
if(NOT IS_ABSOLUTE "${_BisonInput}")
set(_BisonInput "${CMAKE_CURRENT_SOURCE_DIR}/${_BisonInput}")
endif()
endif()
unset(_BISON_CMP0088)
add_custom_command(OUTPUT ${BISON_TARGET_outputs}
COMMAND ${CMAKE_COMMAND} -E env "M4=${BISON_M4_EXECUTABLE}"
${BISON_EXECUTABLE} ${BISON_TARGET_cmdopt} -o ${BisonOutput} ${_BisonInput}
VERBATIM
DEPENDS ${_BisonInput}
COMMENT "[BISON][${Name}] Building parser with bison ${BISON_VERSION}"
WORKING_DIRECTORY ${_BISON_WORKING_DIRECTORY})
unset(_BISON_WORKING_DIRECTORY)
# define target variables
set(BISON_${Name}_DEFINED TRUE)
set(BISON_${Name}_INPUT ${_BisonInput})
set(BISON_${Name}_OUTPUTS ${BISON_TARGET_outputs} ${BISON_TARGET_extraoutputs})
set(BISON_${Name}_COMPILE_FLAGS ${BISON_TARGET_cmdopt})
set(BISON_${Name}_OUTPUT_SOURCE "${BisonOutput}")
set(BISON_${Name}_OUTPUT_HEADER "${BISON_TARGET_output_header}")
unset(_BisonInput)
endif()
endmacro()
#
#============================================================
endif()
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON REQUIRED_VARS BISON_EXECUTABLE
VERSION_VAR BISON_VERSION)