1616import glob
1717import logging
1818import os
19+ from pathlib import Path
1920import re
21+ import subprocess
2022import sys
2123from typing import Dict
2224
3739
3840ATTR_REGEX = re .compile (r",?\s*(?P<key>[^=]+)=(?P<value>[^,]+)" )
3941
42+ BAZEL_MODULE_REGEX = re .compile (r'\s*commit\s*=\s*\"(?P<commit>[0-9a-fA-F]+)\"\s*,\s*#\s*keep-in-sync-with-submodule:\s*(?P<dependency>\S*)' )
43+
44+ BAZEL_VERSION_REGEX = re .compile (r'module\(\s*name\s*=\s*"pico-sdk",\s*version\s*=\s*"(?P<sdk_version>[^"]+)",?\s*\)' )
45+
46+ CMAKE_VERSION_REGEX = re .compile (r'^[^#]*set\(PICO_SDK_VERSION_(?P<part>\S+)\s+(?P<value>\S+)\)' )
47+
4048# Sometimes the build systems are supposed to be implemented differently. This
4149# allowlist permits the descriptions to differ between CMake and Bazel.
4250BUILD_SYSTEM_DESCRIPTION_DIFFERENCE_ALLOWLIST = (
6068 "PICO_TOOLCHAIN_PATH" ,
6169 # Bazel uses native --platforms mechanics.
6270 "PICO_PLATFORM" ,
63- # TODO: No built-in, pre-configured clang offering for Bazel yet .
71+ # Named PICO_TOOLCHAIN in Bazel.
6472 "PICO_COMPILER" ,
6573 # Entirely irrelevant to Bazel, use Bazel platforms:
6674 # https://bazel.build/extending/platforms
8795 "PICO_DEFAULT_PIOASM_OUTPUT_FORMAT" ,
8896 # Bazel always has picotool.
8997 "PICO_NO_PICOTOOL" ,
90- # TODO: Eventualy support.
91- "PICO_NO_COPRO_DIS" ,
92- "PICO_DEFAULT_RP2350_PLATFORM" ,
93- "PICO_GCC_TRIPLE" ,
94- "PICO_NO_FLASH" ,
95- "PICO_COPY_TO_RAM" ,
96- "PICO_RP2350_ARM_S_CONFIG_HEADER_FILES" ,
97- "PICO_RP2350_RISCV_CONFIG_HEADER_FILES" ,
98+ # These aren't supported as build flags in Bazel. Prefer to
99+ # set these in board header files like other SDK defines.
100+ "CYW43_DEFAULT_PIN_WL_REG_ON" ,
101+ "CYW43_DEFAULT_PIN_WL_DATA_OUT" ,
102+ "CYW43_DEFAULT_PIN_WL_DATA_IN" ,
103+ "CYW43_DEFAULT_PIN_WL_HOST_WAKE" ,
104+ "CYW43_DEFAULT_PIN_WL_CLOCK" ,
105+ "CYW43_DEFAULT_PIN_WL_CS" ,
106+ "CYW43_PIO_CLOCK_DIV_INT" ,
107+ "CYW43_PIO_CLOCK_DIV_FRAC" ,
108+ "CYW43_PIO_CLOCK_DIV_DYNAMIC" ,
98109)
99110
100111BAZEL_ONLY_ALLOWLIST = (
@@ -181,17 +192,17 @@ def FindKnownOptions(option_pattern_matcher, file_paths):
181192 return options
182193
183194
184- def OptionsAreEqual (bazel_option , cmake_option ):
195+ def OptionsAreEqual (bazel_option , cmake_option , warnings_as_errors ):
185196 if bazel_option is None :
186197 if cmake_option .name in CMAKE_ONLY_ALLOWLIST :
187198 return True
188199 _LOG .warning (f" { cmake_option .name } does not exist in Bazel" )
189- return False
200+ return not warnings_as_errors
190201 elif cmake_option is None :
191202 if bazel_option .name in BAZEL_ONLY_ALLOWLIST :
192203 return True
193204 _LOG .warning (f" { bazel_option .name } does not exist in CMake" )
194- return False
205+ return not warnings_as_errors
195206 elif not bazel_option .matches (cmake_option ):
196207 _LOG .error (" Bazel and CMAKE definitions do not match:" )
197208 _LOG .error (f" [CMAKE] { bazel_option } " )
@@ -201,7 +212,7 @@ def OptionsAreEqual(bazel_option, cmake_option):
201212 return True
202213
203214
204- def CompareOptions (bazel_pattern , bazel_files , cmake_pattern , cmake_files ):
215+ def CompareOptions (bazel_pattern , bazel_files , cmake_pattern , cmake_files , warnings_as_errors = True ):
205216 bazel_options = FindKnownOptions (bazel_pattern , bazel_files )
206217 cmake_options = FindKnownOptions (cmake_pattern , cmake_files )
207218
@@ -210,10 +221,72 @@ def CompareOptions(bazel_pattern, bazel_files, cmake_pattern, cmake_files):
210221 both .update (bazel_options )
211222 both .update (cmake_options )
212223 for k in both .keys ():
213- if not OptionsAreEqual (bazel_options .get (k , None ), cmake_options .get (k , None )):
224+ if not OptionsAreEqual (
225+ bazel_options .get (k , None ),
226+ cmake_options .get (k , None ),
227+ warnings_as_errors ,
228+ ):
214229 are_equal = False
215230 return are_equal
216231
232+ def CompareExternalDependencyVersions ():
233+ pattern = re .compile (BAZEL_MODULE_REGEX )
234+ all_okay = True
235+ with open (Path (SDK_ROOT ) / "MODULE.bazel" , "r" ) as bazel_module_file :
236+ for line in bazel_module_file :
237+ maybe_match = pattern .match (line )
238+ if not maybe_match :
239+ continue
240+
241+ current_submodule_pin = subprocess .run (
242+ ("git" , "-C" , SDK_ROOT , "rev-parse" , f'HEAD:lib/{ maybe_match .group ("dependency" )} ' ),
243+ text = True ,
244+ check = True ,
245+ capture_output = True ,
246+ ).stdout .strip ()
247+ if current_submodule_pin != maybe_match .group ("commit" ):
248+ _LOG .error (" External pins for %s do not match:" , maybe_match .group ("dependency" ))
249+ _LOG .error (" [CMAKE] %s" , current_submodule_pin )
250+ _LOG .error (" [BAZEL] %s" , maybe_match .group ("commit" ))
251+ all_okay = False
252+ else :
253+ _LOG .info (" External pins for %s match!" , maybe_match .group ("dependency" ))
254+
255+ return all_okay
256+
257+ def CompareSdkVersion ():
258+ # Find version string specified in Bazel.
259+ bazel_module_file_path = Path (SDK_ROOT ) / "MODULE.bazel"
260+ bazel_module_file_contents = bazel_module_file_path .read_text ()
261+ bazel_sdk_version = BAZEL_VERSION_REGEX .search (bazel_module_file_contents )
262+ if not bazel_sdk_version :
263+ _LOG .error (" Failed to find Bazel Pico SDK version string" )
264+ return False
265+ bazel_version_string = bazel_sdk_version .group ("sdk_version" )
266+
267+ # Find version string specified in CMake.
268+ cmake_version_parts = {}
269+ with open (Path (SDK_ROOT ) / "pico_sdk_version.cmake" , "r" ) as cmake_version_file :
270+ for line in cmake_version_file :
271+ match = CMAKE_VERSION_REGEX .match (line )
272+ if match :
273+ cmake_version_parts [match .group ("part" )] = match .group ("value" )
274+ if len (cmake_version_parts ) < 3 :
275+ _LOG .error (" Failed to find CMake Pico SDK version string" )
276+ return False
277+ cmake_version_string = "." .join ((
278+ cmake_version_parts ["MAJOR" ],
279+ cmake_version_parts ["MINOR" ],
280+ cmake_version_parts ["REVISION" ],
281+ ))
282+ if "PRE_RELEASE_ID" in cmake_version_parts :
283+ cmake_version_string += "-" + cmake_version_parts ["PRE_RELEASE_ID" ]
284+
285+ if cmake_version_string != bazel_version_string :
286+ _LOG .error (" Declared CMake SDK version is %s and Bazel is %s" , cmake_version_string , bazel_version_string )
287+ return False
288+
289+ return True
217290
218291def compare_build_systems ():
219292 cmake_files = [
@@ -227,20 +300,35 @@ def compare_build_systems():
227300 for f in glob .glob (os .path .join (SDK_ROOT , p ), recursive = True )
228301 ]
229302
230- _LOG .info ("[1/2] Checking build system configuration flags..." )
231- build_options_ok = CompareOptions (
232- "PICO_BAZEL_CONFIG" , bazel_files , "PICO_CMAKE_CONFIG" , cmake_files
233- )
303+ results = []
304+ _LOG .info ("[1/3] Checking build system configuration flags..." )
305+ results .append (CompareOptions (
306+ "PICO_BAZEL_CONFIG" ,
307+ bazel_files ,
308+ "PICO_CMAKE_CONFIG" ,
309+ cmake_files ,
310+ # For now, allow CMake and Bazel to go out of sync when it comes to
311+ # build configurability since it's a big ask to make contributors
312+ # implement the same functionality in both builds.
313+ warnings_as_errors = False ,
314+ ))
234315
235- _LOG .info ("[2/2 ] Checking build system defines..." )
236- build_defines_ok = CompareOptions (
316+ _LOG .info ("[2/4 ] Checking build system defines..." )
317+ results . append ( CompareOptions (
237318 "PICO_BUILD_DEFINE" , bazel_files , "PICO_BUILD_DEFINE" , cmake_files
238- )
319+ ))
320+
321+ _LOG .info ("[3/4] Checking submodule pins..." )
322+ results .append (CompareExternalDependencyVersions ())
323+
324+ _LOG .info ("[4/4] Checking version strings..." )
325+ results .append (CompareSdkVersion ())
239326
240- if build_options_ok and build_defines_ok :
241- _LOG .info ("OK " )
327+ if False not in results :
328+ _LOG .info ("Passed with no blocking failures " )
242329 return 0
243330
331+ _LOG .error ("One or more blocking failures detected" )
244332 return 1
245333
246334
0 commit comments