Skip to content

Commit

Permalink
pw_build: CMake support for pw_cc_blob_library
Browse files Browse the repository at this point in the history
- Define the pw_cc_blob_library function that is equivalent to the GN
  template.
- Create pw_require_args(), a macro used to check that certain arguments
  were provided to a function.

Change-Id: I5ad3e3c5a08e20f47a2752c5059eac1cdc3d81c2
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/101223
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
  • Loading branch information
255 authored and CQ Bot Account committed Jul 8, 2022
1 parent 80deda2 commit 99ad662
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 0 deletions.
27 changes: 27 additions & 0 deletions pw_build/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
# IMPORTANT: The compilation flags in this file must be kept in sync with
# the GN flags //pw_build/BUILD.gn.

include("$ENV{PW_ROOT}/pw_build/cc_blob_library.cmake")
include("$ENV{PW_ROOT}/pw_build/pigweed.cmake")

# Target that specifies the standard Pigweed build options.
add_library(pw_build INTERFACE)
target_compile_options(pw_build INTERFACE "-g")
Expand Down Expand Up @@ -112,3 +115,27 @@ target_compile_options(pw_build.cpp17
# Create an empty source file and library for general use.
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/empty_file.c" "")
add_library(pw_build.empty OBJECT "${CMAKE_CURRENT_BINARY_DIR}/empty_file.c" "")

pw_add_test(pw_build.cc_blob_library_test
SOURCES
cc_blob_library_test.cc
DEPS
pw_build.test_blob
GROUPS
modules
pw_build
)

pw_cc_blob_library(pw_build.test_blob
HEADER
pw_build/test_blob.h
NAMESPACE
test::ns
BLOB
SYMBOL_NAME kFirstBlob0123
PATH test_blob_0123.bin
ALIGNAS 512
BLOB
SYMBOL_NAME kSecondBlob0123
PATH test_blob_0123.bin
)
171 changes: 171 additions & 0 deletions pw_build/cc_blob_library.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

include_guard(GLOBAL)

cmake_minimum_required(VERSION 3.20) # string(JSON)

include("$ENV{PW_ROOT}/pw_build/pigweed.cmake")

# Turns binary blobs into a C++ library of hard-coded byte arrays. The byte
# arrays are constant initialized and are safe to access at any time, including
# before main().
#
# Args:
#
# HEADER
#
# The header file to generate. Users will include this file exactly as it is
# written here to reference the byte arrays.
#
# NAMESPACE
#
# The C++ namespace in which to place the generated blobs.
#
# BLOB
#
# A blob to be transformed from file to byte array. Multiple blobs may be
# specified.
#
# Blob args:
#
# SYMBOL_NAME
#
# The C++ symbol name for the byte array.
#
# PATH
#
# The file path for the binary blob.
#
# LINKER_SECTION [optional]
#
# If present, places the byte array in the specified linker section.
#
# ALIGNAS [optional]
#
# If present, the byte array is aligned as specified. The value of this
# argument is used verbatim in an alignas() specifier for the blob
# byte array.
#
function(pw_cc_blob_library NAME)
cmake_parse_arguments(PARSE_ARGV 1 arg "" "HEADER;NAMESPACE" "")

set(blobs ${arg_UNPARSED_ARGUMENTS})
set(blob_files "")
set(blob_index 0)
set(json_blobs "[]") # Create a JSON list of blobs

pw_require_args("" arg_ HEADER NAMESPACE)

while(NOT "${blobs}" STREQUAL "")
list(POP_FRONT blobs first_arg)

if(NOT "${first_arg}" STREQUAL BLOB)
message(FATAL_ERROR "Invalid syntax in pw_cc_blob_library: "
"Expected 'BLOB', found '${first_arg}'.")
endif()

list(FIND blobs BLOB blob_end)
list(SUBLIST blobs 0 "${blob_end}" current_blob)

cmake_parse_arguments(
blob_arg "" "SYMBOL_NAME;PATH;LINKER_SECTION;ALIGNAS" "" "${current_blob}"
)

if(NOT "${blob_arg_UNPARSED_ARGUMENTS}" STREQUAL "")
message(FATAL_ERROR "Unexpected BLOB arguments in pw_cc_blob_library: "
"${blob_arg_UNPARSED_ARGUMENTS}")
endif()

pw_require_args("BLOB args for ${CMAKE_CURRENT_FUNCTION}"
blob_arg_ PATH SYMBOL_NAME)

cmake_path(ABSOLUTE_PATH blob_arg_PATH)
list(APPEND blob_files "${blob_arg_PATH}")

set(json_blob "{}")
_pw_json_set_string_key(json_blob file_path "${blob_arg_PATH}")
_pw_json_set_string_key(json_blob symbol_name "${blob_arg_SYMBOL_NAME}")

if(NOT "${blob_arg_ALIGNAS}" STREQUAL "")
_pw_json_set_string_key(json_blob alignas "${blob_arg_ALIGNAS}")
endif()

if(NOT "${blob_arg_LINKER_SECTION}" STREQUAL "")
_pw_json_set_string_key(
json_blob linker_section "${blob_arg_LINKER_SECTION}")
endif()

string(JSON json_blobs SET "${json_blobs}" "${blob_index}" "${json_blob}")

if("${blob_end}" EQUAL -1)
break()
endif()

list(SUBLIST blobs "${blob_end}" -1 blobs)
math(EXPR blob_index "${blob_index}+1")
endwhile()

set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/${NAME}")
set(blob_json_file "${out_dir}/blobs.json")

file(WRITE "${blob_json_file}" "${json_blobs}")
set_property( # Ensure the file is regenerated by CMake if it is deleted.
DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${blob_json_file}")

set(generated_header "${out_dir}/public/${arg_HEADER}")

cmake_path(GET arg_HEADER STEM filename)
set(generated_source "${out_dir}/${filename}.cc")

add_custom_command(
COMMAND
python3
"$ENV{PW_ROOT}/pw_build/py/pw_build/generate_cc_blob_library.py"
--blob-file "${blob_json_file}"
--header-include "${arg_HEADER}"
--out-header "${generated_header}"
--out-source "${generated_source}"
--namespace "${arg_NAMESPACE}"
DEPENDS
"$ENV{PW_ROOT}/pw_build/py/pw_build/generate_cc_blob_library.py"
"${blob_json_file}"
${blob_files}
OUTPUT
"${generated_header}"
"${generated_source}"
)

add_custom_target("${NAME}._gen"
DEPENDS
"${generated_header}"
"${generated_source}"
)

pw_add_library("${NAME}" STATIC
SOURCES
"${generated_source}"
PUBLIC_INCLUDES
"${out_dir}/public"
PUBLIC_DEPS
pw_polyfill
pw_preprocessor
)
add_dependencies("${NAME}" "${NAME}._gen")
endfunction(pw_cc_blob_library)

# Sets a key with a string value in a JSON object.
macro(_pw_json_set_string_key json_var key value)
string(JSON "${json_var}" SET "${${json_var}}" "${key}" "\"${value}\"")
endmacro()
3 changes: 3 additions & 0 deletions pw_build/docs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ passed-in files as arrays of ``std::byte``.
The blob byte arrays are constant initialized and are safe to access at any
time, including before ``main()``.

``pw_cc_blob_library`` is also available in the CMake build. It is provided by
``pw_build/cc_blob_library.cmake``.

**Arguments**

* ``blobs``: A list of GN scopes, where each scope corresponds to a binary blob
Expand Down
32 changes: 32 additions & 0 deletions pw_build/pigweed.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,38 @@ macro(pw_parse_arguments_strict function start_arg options one multi)
endif()
endmacro()

# Checks that one or more variables are set. This is used to check that
# arguments were provided to a function. Fails with FATAL_ERROR if
# ${ARG_PREFIX}${name} is empty. The FUNCTION_NAME is used in the error message.
# If FUNCTION_NAME is "", it is set to CMAKE_CURRENT_FUNCTION.
#
# Usage:
#
# pw_require_args(FUNCTION_NAME ARG_PREFIX ARG_NAME [ARG_NAME ...])
#
# Examples:
#
# # Checks that arg_FOO is non-empty, using the current function name.
# pw_require_args("" arg_ FOO)
#
# # Checks that FOO and BAR are non-empty, using function name "do_the_thing".
# pw_require_args(do_the_thing "" FOO BAR)
#
macro(pw_require_args FUNCTION_NAME ARG_PREFIX)
if("${FUNCTION_NAME}" STREQUAL "")
set(_pw_require_args_FUNCTION_NAME "${CMAKE_CURRENT_FUNCTION}")
else()
set(_pw_require_args_FUNCTION_NAME "${FUNCTION_NAME}")
endif()

foreach(name IN ITEMS ${ARGN})
if("${${ARG_PREFIX}${name}}" STREQUAL "")
message(FATAL_ERROR "A value must be provided for ${name} in "
"${_pw_require_args_FUNCTION_NAME}.")
endif()
endforeach()
endmacro()

# Automatically creates a library and test targets for the files in a module.
# This function is only suitable for simple modules that meet the following
# requirements:
Expand Down

0 comments on commit 99ad662

Please sign in to comment.