The utility packgen assists the generation of a CMSIS-Pack from CMakeLists
according to the standard
format
by reading a manifest YAML file with metadata and running the CMake generation
step to retrieve CMake targets build information, namely source files, include
paths and defines. It distributed as part of
the CMSIS-Toolbox.
- CMake: https://cmake.org/
- Ninja: https://ninja-build.org/
NOTE: Make sure the CMake project has been previously configured and dependencies have been installed. It is a requirement to be able to successfully run the CMake generation step in the current environment.
For validating and compressing pack files, the packchk and 7-zip utilities
shall be in the PATH system environment variable:
packgen [-V] [--version] [-h] [--help]
[OPTIONS...] manifest.yml
packgen options:
-s, --source arg Source root folder
-o, --output arg Output folder
-i, --include arg PDSC file(s) for external dependency check
-r, --regenerate Regenerate CMake targets
-v, --verbose Verbose mode
-c, --nocheck Skip pack check
-z, --nozip Skip *.pack file creation
-h, --help Print usage
-V, --version Print versionThe example CMakeTestProject provides an input manifest.yml describing components and their inter-dependencies to be generated from the CMakeLists.txt.
The demonstration pack can be generated by executing the packgen tool in the example directory:
packgen manifest.yml -o OutputFolderThe generated pack files will be stored in the OutputFolder folder. The generated PDSC file should be identical to
ARM.TestPack.pdsc.
The contents of a component are retrieved from the input manifest file and
from the CMake targets associated with it. If several CMake targets are listed,
the build information (source files, include paths and defines) will be merged.
The build information of other components or CMake targets listed as
dependencies will be filtered out.
External dependencies of a component can be added to the conditions list.
When an internal component is listed as dependency, a require condition is
automatically inserted. For checking the correctness of external dependencies
the path of external PDSC files (absolute or relative to the working directory)
can be specified with the command line option --include.
Files that are not among the build information such as config files can still
join a component by adding them to the files list.
After changing CMakeLists and/or other included CMake files, use the tool
option --regenerate to rerun the CMake generation step. When changing only
the input YAML file there is no need to regenerate CMake targets.
Multiple sets of CMake options are supported and shall be inserted in the
manifest file in the top level build mapping. The set(s) of options to be
considered in the generation of a given component shall be inserted in the
build field under the components mapping, while the operation to be
applied to them can be chosen as intersection or difference.
The manifest.yml file shall be placed alongside the entry point CMakeLists.txt.
The command line option --source shall be used to indicate the root (top-level)
source folder when the CMakeLists.txt and manifest.yml are not located at the
root folder. Paths inside the manifest.yml file shall be relative to the root folder.
The contents of include paths to be recursively copied into the pack can be
filtered by setting allowed file extensions in the extensions tag, the
default are .h and .hpp. These extensions are also used for handling file
categories of source files coming from CMake targets.
The YAML structure is described below.
build:
- name: <value>
options: <value>
packs:
- name: <value>
description: <value>
vendor: <value>
license: <value>
url: <value>
releases:
- version: <value>
date: <value>
description: <value>
requirements:
packages:
- attributes: {<key:value list>}
compilers:
- attributes: {<key:value list>}
languages:
- attributes: {<key:value list>}
taxonomy:
- attributes: {<key:value list>}
description: <value>
apis:
- name: <value>
attributes: {<key:value list>}
description: <value>
files:
- name: <value>
attributes: {<key:value list>}
extensions: [<list>]
components:
- name: <value>
target: [<list>]
build: [<list>]
operation: [<intersection|difference>]
attributes: {<key:value list>}
description: <value>
dependencies: [<list>]
conditions:
- <require|accept|deny>: {<key:value list>}
files:
- name: <value>
attributes: {<key:value list>}
conditions:
- <require|accept|deny>: {<key:value list>}
extensions: [<list>]| Argument | Description |
|---|---|
| name | Name of a set of CMake options. Multiple sets are supported |
| options | List of CMake options |
| Argument | Description |
|---|---|
| name | Unique name of the Software Pack |
| description | Description of the Software Pack |
| vendor | Name of the supplier or vendor of the Software Pack |
| license | Path to license document |
| url | HTTP URL or file URI location of the Software Pack |
| requirements | Lists of requirements |
| releases | List of Software Pack releases |
| components | List of Software Pack components |
| Argument | Description |
|---|---|
| packages | List of required package attributes |
| compilers | List of required compiler attributes |
| languages | List of required language attributes |
| Argument | Description |
|---|---|
| version | Semantic version number, see Version Type |
| date | Release date in the format YYYY-MM-DD |
| description | Release notes |
| Argument | Description |
|---|---|
| attributes | Typically Cclass and Cgroup, see taxonomy |
| description | Taxonomy entry brief description |
| Argument | Description |
|---|---|
| name | Unique name of the API |
| description | API brief description |
| attributes | List of API attributes |
| files | List of API files |
| extensions | List of header file extensions for recursive copy |
| Argument | Description |
|---|---|
| name | Unique name of the component |
| target | List of CMake targets associated with the component |
| build | List of build options sets associated with the component |
| operation | Obtain the intersection or the difference of build options sets |
| description | Component brief description |
| dependencies | List of component dependencies. Other components or CMake targets are accepted. |
| attributes | List of component attributes |
| conditions | List of component conditions |
| files | List of component files |
| extensions | List of header file extensions for categorization and recursive copy |
| Argument | Description |
|---|---|
| Cclass | Component class |
| Cgroup | Component group |
| Csubgroup | Component subgroup |
| Cvariant | Component variant |
| Cversion | Component version |
| Argument | Description |
|---|---|
| name | File path relative to the pack root directory |
| attributes | List of file attributes |
| conditions | List of file conditions |
| Argument | Description |
|---|---|
| category | Defines the purpose of the file. |
| attr | Defines the special use and handling of a file. |
| version | File-specific version information. |
| Argument | Description |
|---|---|
| <rule> | It must be require, accept or deny |
| <key:value list> | Typically external component attributes, but not limited to it. |
For further info please consult the PDSC specification, for example:
When starting the process of generating a new pack, the following workflow is recommended:
-
In the project's root folder side by side with the
CMakeLists.txtcreate an inputYAMLfile with the minimal required fields, for instance:build: - name: "build1" options: "cmake -G Ninja" packs: - name: "TestPack" description: "TestPack description" vendor: "ARM" license: "LICENSE" url: "http://arm.com/" releases: - version: "1.0.0" date: "2021-08-10" description: "Initial release"
-
Run
packgenin verbose mode to obtain a list of CMake targets:packgen manifest.yml -o OutputFolder -v -c -z >log.txtIn the example's case the following information will be printed:
log.txtTARGET: lib1 BUILD: build1 src: lib1/src/lib1.cpp inc: lib1/inc TARGET: lib2 BUILD: build1 src: lib2/src/lib2.cpp inc: lib2/inc inc: lib3/inc inc: lib4/inc dep: lib3 dep: lib4 TARGET: lib3 BUILD: build1 src: lib3/src/lib3.cpp inc: lib3/inc TARGET: lib4 BUILD: build1 src: lib4/src/lib4.cpp inc: lib4/inc TARGET: lib5 BUILD: build1 src: lib5/src/lib5.cpp inc: lib5/inc
-
Describe the
componentsto be generated by assigning targets to be exposed or filtered out, respectively in the componentstargetordependenciesfields.In the example below the content of the generated component3 is merged from
lib4andlib5:manifest.ymlcomponents: - name: component3 target: [lib4, lib5] attributes: {Cclass: "Class3", Cgroup: "Group3", Csub: "Subgroup3", Cversion: "3.3.3"} description: "Component3"
ARM.TestPack.pdsc<component Cclass="Class3" Cgroup="Group3" Csub="Subgroup3" Cversion="3.3.3"> <description>Component3</description> <files> <file category="source" name="lib4/src/lib4.cpp"/> <file category="source" name="lib5/src/lib5.cpp"/> <file category="include" name="lib4/inc/"/> <file category="include" name="lib5/inc/"/> </files> </component>
The example
CMakeTestMultipleBuilds has a
CMakeLists.txt producing a library with different sources and
include paths depending on the DEVICE option that can assume the values DEV1, DEV2 or DEV3:
if(DEVICE STREQUAL "DEV1")
add_library(lib STATIC src/core.cpp
src/dev1.cpp)
target_include_directories(lib PUBLIC inc
inc1)
elseif(DEVICE STREQUAL "DEV2")
add_library(lib STATIC src/core.cpp
src/dev2.cpp)
target_include_directories(lib PUBLIC inc
inc2)
elseif(DEVICE STREQUAL "DEV3")
add_library(lib STATIC src/core.cpp
src/dev3.cpp)
target_include_directories(lib PUBLIC inc
inc3)
endif()It means the CMake generation step has to be executed several times to cover all the possible results. To instruct the tool to run all the configurations, the manifest.yml has the following build options mapping:
build:
- name: build-dev1
options: "cmake -G Ninja -DDEVICE=DEV1"
- name: build-dev2
options: "cmake -G Ninja -DDEVICE=DEV2"
- name: build-dev3
options: "cmake -G Ninja -DDEVICE=DEV3"The description of every component indicates the sets of build options that will be associated and which
operation will be applied to them, intersection or difference. Note in case of difference the order of sets
is important:
components:
- name: core
build: ["build-dev1", "build-dev2", "build-dev3"]
operation: intersection
- name: device1
build: ["build-dev1", "build-dev2", "build-dev3"]
operation: difference
- name: device2
build: ["build-dev2", "build-dev1", "build-dev3"]
operation: difference
- name: device3
build: ["build-dev3", "build-dev1", "build-dev2"]
operation: differenceRun packgen in verbose mode to print a list of CMake targets and the of generated components:
packgen manifest.yml -o OutputFolder -v -c -z > log.txtThe core component with common elements is generated by using the
intersection operation, while each other device component is created
with the difference operation:
TARGET: lib
BUILD: build-dev1
src: src/core.cpp
src: src/dev1.cpp
inc: inc
inc: inc1
TARGET: lib
BUILD: build-dev2
src: src/core.cpp
src: src/dev2.cpp
inc: inc
inc: inc2
TARGET: lib
BUILD: build-dev3
src: src/core.cpp
src: src/dev3.cpp
inc: inc
inc: inc3
COMPONENT: core
src: src/core.cpp
inc: inc
COMPONENT: device1
src: src/dev1.cpp
inc: inc1
COMPONENT: device2
src: src/dev2.cpp
inc: inc2
COMPONENT: device3
src: src/dev3.cpp
inc: inc3