From c9e3ae8c9a507883318909e1ef4c8398fc521ecd Mon Sep 17 00:00:00 2001 From: Catherine Date: Fri, 5 Jun 2026 15:52:48 +0000 Subject: [PATCH 1/3] 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.) --- cmake/FindBISON.cmake | 346 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 cmake/FindBISON.cmake diff --git a/cmake/FindBISON.cmake b/cmake/FindBISON.cmake new file mode 100644 index 000000000..11851fd05 --- /dev/null +++ b/cmake/FindBISON.cmake @@ -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( + [COMPILE_FLAGS ] + [DEFINES_FILE ] + [VERBOSE []] + [REPORT_FILE ] + ) + +which will create a custom rule to generate a parser. ```` is +the path to a yacc file. ```` 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 `` + Specify flags to be added to the ``bison`` command line. + +``DEFINES_FILE `` + .. versionadded:: 3.4 + + Specify a non-default header ```` to be generated by ``bison``. + +``VERBOSE []`` + Tell ``bison`` to write a report file of the grammar and parser. + + .. deprecated:: 3.7 + If ```` is given, it specifies path the report file is copied to. + ``[]`` is left for backward compatibility of this module. + Use ``VERBOSE REPORT_FILE ``. + +``REPORT_FILE `` + .. versionadded:: 3.7 + + Specify a non-default report ````, if generated. + +The macro defines the following variables: + +``BISON__DEFINED`` + ``True`` is the macro ran successfully + +``BISON__INPUT`` + The input source file, an alias for + +``BISON__OUTPUT_SOURCE`` + The source file generated by bison + +``BISON__OUTPUT_HEADER`` + The header file generated by bison + +``BISON__OUTPUTS`` + All files generated by bison including the source, the header and the report + +``BISON__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 [] hack: 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 []] 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 + 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) From d50dc9a461ed96944c3e81666db378804c6a8320 Mon Sep 17 00:00:00 2001 From: Catherine Date: Fri, 5 Jun 2026 15:54:14 +0000 Subject: [PATCH 2/3] CMake: add all Homebrew packages to root search path. Homebrew doesn't provide a merged (FHS) prefix and tools installed from it cannot be expected to appear on PATH. Furthermore, XCode provides some tools and headers (Flex, Bison) which must not be used if a Homebrew alternative is installed. --- CMakeLists.txt | 6 ++++++ cmake/UseHomebrew.cmake | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 cmake/UseHomebrew.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 3deacd232..69b16746c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ include(YosysLinkTarget) include(YosysAbc) include(YosysAbcSubmodule) include(YosysVerific) +include(UseHomebrew) # Build options. set(YOSYS_COMPILER_LAUNCHER "" CACHE STRING "Compiler launcher (ccache, sccache)") @@ -162,6 +163,11 @@ if (MINGW AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSIO endif() # Required dependencies. +if (APPLE) + # In practice, we can't expect paths to Homebrew packages to be configured. + use_homebrew() +endif() + find_package(FLEX) set_package_properties(FLEX PROPERTIES URL "https://github.com/westes/flex" diff --git a/cmake/UseHomebrew.cmake b/cmake/UseHomebrew.cmake new file mode 100644 index 000000000..e94441109 --- /dev/null +++ b/cmake/UseHomebrew.cmake @@ -0,0 +1,22 @@ +# Syntax: +# +# use_homebrew([ROOT ]) +# +# Includes all packages installed in `` (`/opt/homebrew/Cellar` if not specified) +# in `CMAKE_FIND_ROOT_PATH`. +# +function(use_homebrew) + cmake_parse_arguments(PARSE_ARGV 0 arg "" "ROOT" "") + if (NOT arg_ROOT) + set(arg_ROOT /opt/homebrew/Cellar) + endif() + + file(GLOB package_roots ${arg_ROOT}/*/*) # e.g. `/opt/homebrew/Cellar/bison/3.8.2/` + foreach (package_root ${package_roots}) + if (IS_DIRECTORY ${package_root}) + list(APPEND CMAKE_FIND_ROOT_PATH ${package_root}) + endif() + endforeach() + + return(PROPAGATE CMAKE_FIND_ROOT_PATH) +endfunction() From 39ecd0d93f18e55065ace90e421fefebfeac9e68 Mon Sep 17 00:00:00 2001 From: Catherine Date: Fri, 5 Jun 2026 15:55:20 +0000 Subject: [PATCH 3/3] CMake: specify Flex and Bison version requirements. macOS ships Bison 2.6, which is too old for Yosys. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 69b16746c..eb67b393c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -168,7 +168,7 @@ if (APPLE) use_homebrew() endif() -find_package(FLEX) +find_package(FLEX 2.6) set_package_properties(FLEX PROPERTIES URL "https://github.com/westes/flex" DESCRIPTION "The Fast Lexical Analyzer" @@ -176,7 +176,7 @@ set_package_properties(FLEX PROPERTIES TYPE REQUIRED ) -find_package(BISON) +find_package(BISON 3.6) set_package_properties(BISON PROPERTIES URL "https://www.gnu.org/software/bison/" DESCRIPTION "The Yacc-compatible Parser Generator"