Skip to content

Commit eafaff9

Browse files
Improve ergonomics of using TheRock as a development driver for sub-projects. (ROCm#1242)
The motivation of this change is to enable the following developer productivity features: * Development in a sub-project `build` directory should cause the super-project to see the sub-project as out of date and take appropriate action. This is done by injecting an ALL `therock-touch` target into each sub-project so that `ninja` will invalidate the super-project stage-install state, causing a re-install and rebuild of dependents. * One stop commands to build and distribute in a sub-project without switching back and forth. Users can now just run `ninja therock-dist` in any sub-project to build and distribute their local changes (i.e. to `build/dist/rocm`, etc). * More coverage of `expunge` targets, making sure that project state can be reset more completely. Key changes: * Removes some `therock-` prefixes from top level convenience targets. * Defines `artifacts`, `archives`, `dist`, and `expunge` top level convenience targets explicitly. * Adds `expunge` steps to all phases of build, artifact assembly and dist population. * Removes the formal sub-project `+dist` phase, collapsing the creation of the local `dist` directory into the `+stage` (install) step. This separation was done early on when creating the dist/ tree was slow but it doesn't pay for itself anymore and was becoming hard to distinguish the different "dist" concepts in the project. * Artifacts can be declared as belonging to different distributions (default "rocm"), resulting in population of different trees in `build/dist/{distribution}`. This was a TODO from the beginning. * `{subproject}+dist` now is a convenience target that builds all artifacts the project is a part of and populates the `build/dist/{distname}` directory. * Distributions (i.e. `build/dist/rocm`) are now populated incrementally as artifact dependencies are met instead of as part of one top level target which requires the whole project to be (re)built. * New targets `therock-touch` and `therock-dist` are injected into every sub-project, allowing development to be done completely in a sub-project build directory while keeping super-project level artifacts and distributions populated. This should be NFC for all automation since the new barename targets (dist, archives) are aliased to their originals. We could simplify this in followups.
1 parent 87ddf70 commit eafaff9

File tree

8 files changed

+194
-101
lines changed

8 files changed

+194
-101
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ cmake_policy(SET CMP0135 NEW)
1616
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
1717
include(CMakeDependentOption)
1818
include(ExternalProject)
19+
include(therock_default_targets)
1920
include(therock_amdgpu_targets)
2021
include(therock_artifacts)
2122
include(therock_compiler_config)
@@ -447,4 +448,3 @@ endif()
447448
# Finalization
448449
################################################################################
449450
therock_subproject_merge_compile_commands()
450-
therock_create_dist()

build_tools/fileset_tool.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,9 @@ def _do_artifact_flatten(args):
269269
flattener(*args.artifact)
270270
relpaths = list(flattener.relpaths)
271271
relpaths.sort()
272-
for relpath in relpaths:
273-
print(relpath)
272+
if args.verbose:
273+
for relpath in relpaths:
274+
print(relpath)
274275

275276

276277
def _dup_list_or_str(v: list[str] | str) -> list[str]:

build_tools/tests/fileset_tool_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def testSimpleArtifact(self):
173173
FILESET_TOOL,
174174
"artifact-flatten",
175175
artifact_archive,
176+
"--verbose",
176177
"-o",
177178
flat2_dir,
178179
]

cmake/therock_artifacts.cmake

Lines changed: 84 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,30 @@
22
# Facilities for bundling artifacts for bootstrapping and subsequent CI/CD
33
# phases.
44

5-
# Property containing all artifact directories for all components created via
6-
# therock_provide_artifact(). This is used to populate the dist/rocm directory
7-
# by flattening them all. In the future, we may have multiple dist groups but
8-
# there is just this one for now.
9-
set_property(GLOBAL PROPERTY THEROCK_DIST_ARTIFACT_DIRS)
10-
5+
# therock_provide_artifact
6+
# This populates directories under build/artifacts representing specific
7+
# subsets of the install tree. See docs/development/artifacts.md for further
8+
# design notes on the subsystem.
9+
#
10+
# While artifacts are the primary output of the build system, it is often
11+
# an aid to development to materialize them all locally into a `distribution`.
12+
# These are directories under build/dist/${ARG_DISTRIBUTION} (default "rocm").
13+
#
14+
# All artifact slices of a distribution should be non-overlapping, populating
15+
# some subset of the install directory tree.
16+
#
17+
# This will produce the following convenience targets:
18+
# - artifact-${slice_name} : Populate the build/artifacts/{qualified_name}
19+
# directory. Added as a dependency of the `therock-artifacts` target.
20+
# - archive-${slice_name} : Populate the build/artifacts/{qualified_name}.tar.xz
21+
# archive file. Added as a dependency of the `therock-archives` target.
22+
#
23+
# Convenience targets with a "+expunge" suffix are created to remove corresponding
24+
# files. Invoking the project level "expunge" will depend on all of them.
1125
function(therock_provide_artifact slice_name)
1226
cmake_parse_arguments(PARSE_ARGV 1 ARG
1327
"TARGET_NEUTRAL"
14-
"DESCRIPTOR"
28+
"DESCRIPTOR;DISTRIBUTION"
1529
"COMPONENTS;SUBPROJECT_DEPS"
1630
)
1731

@@ -23,8 +37,8 @@ function(therock_provide_artifact slice_name)
2337
endif()
2438

2539
# Normalize arguments.
26-
set(_target_name "therock-artifact-${slice_name}")
27-
set(_archive_target_name "therock-archive-${slice_name}")
40+
set(_target_name "artifact-${slice_name}")
41+
set(_archive_target_name "archive-${slice_name}")
2842
if(TARGET "${_target_name}")
2943
message(FATAL_ERROR "Artifact slice '${slice_name}' provided more than once")
3044
endif()
@@ -37,12 +51,24 @@ function(therock_provide_artifact slice_name)
3751
endif()
3852
cmake_path(ABSOLUTE_PATH ARG_DESCRIPTOR BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
3953

40-
# We make all artifact slices available as therock-artifacts top-level target.
41-
if(NOT TARGET therock-artifacts)
42-
add_custom_target(therock-artifacts)
54+
if(NOT DEFINED ARG_DISTRIBUTION)
55+
set(ARG_DISTRIBUTION "rocm")
4356
endif()
44-
if(NOT TARGET therock-archives)
45-
add_custom_target(therock-archives)
57+
58+
if(ARG_DISTRIBUTION)
59+
set(_dist_dir "${THEROCK_BINARY_DIR}/dist/${ARG_DISTRIBUTION}")
60+
if(NOT TARGET "dist-${ARG_DISTRIBUTION}")
61+
add_custom_target("dist-${ARG_DISTRIBUTION}" ALL)
62+
add_dependencies(therock-dist "dist-${ARG_DISTRIBUTION}")
63+
64+
# expunge target for the dist
65+
add_custom_target(
66+
"dist-${ARG_DISTRIBUTION}+expunge"
67+
COMMAND
68+
"${CMAKE_COMMAND}" -E rm -rf "${_dist_dir}"
69+
)
70+
add_dependencies(therock-expunge "dist-${ARG_DISTRIBUTION}+expunge")
71+
endif()
4672
endif()
4773

4874
# Determine top-level name.
@@ -57,27 +83,34 @@ function(therock_provide_artifact slice_name)
5783
set(_stamp_file_deps)
5884
_therock_cmake_subproject_deps_to_stamp(_stamp_file_deps "stage.stamp" ${ARG_SUBPROJECT_DEPS})
5985

60-
# Assemble commands.
86+
# Populate commands.
6187
set(_fileset_tool "${THEROCK_SOURCE_DIR}/build_tools/fileset_tool.py")
6288
set(_command_list)
6389
set(_manifest_files)
90+
set(_component_dirs)
6491
foreach(_component ${ARG_COMPONENTS})
6592
set(_component_dir "${THEROCK_BINARY_DIR}/artifacts/${slice_name}_${_component}${_bundle_suffix}")
66-
set_property(GLOBAL APPEND PROPERTY THEROCK_DIST_ARTIFACT_DIRS "${_component_dir}")
93+
list(APPEND _component_dirs "${_component_dir}")
6794
set(_manifest_file "${_component_dir}/artifact_manifest.txt")
6895
list(APPEND _manifest_files "${_manifest_file}")
96+
# Populate the artifact directory.
6997
list(APPEND _command_list
7098
COMMAND "${Python3_EXECUTABLE}" "${_fileset_tool}" artifact
7199
--output-dir "${_component_dir}"
72100
--root-dir "${THEROCK_BINARY_DIR}" --descriptor "${ARG_DESCRIPTOR}"
73101
--component "${_component}"
74102
)
103+
# Populate the corresponding build/dist/DISTRIBUTION directory.
104+
if(ARG_DISTRIBUTION)
105+
list(APPEND _command_list
106+
COMMAND "${Python3_EXECUTABLE}" "${_fileset_tool}" artifact-flatten
107+
-o "${_dist_dir}" ${_component_dirs}
108+
)
109+
endif()
75110
endforeach()
76-
77-
# Set up command.
78111
add_custom_command(
79112
OUTPUT ${_manifest_files}
80-
COMMENT "Merging artifact ${slice_name}"
113+
COMMENT "Populate artifact ${slice_name}"
81114
${_command_list}
82115
DEPENDS
83116
${_stamp_file_deps}
@@ -89,15 +122,20 @@ function(therock_provide_artifact slice_name)
89122
DEPENDS ${_manifest_files}
90123
)
91124
add_dependencies(therock-artifacts "${_target_name}")
125+
if(ARG_DISTRIBUTION)
126+
add_dependencies("dist-${ARG_DISTRIBUTION}" "${_target_name}")
127+
endif()
92128

93-
### Generate artifact archive commands.
129+
# Generate artifact archive commands.
94130
set(_archive_files)
131+
set(_archive_sha_files)
95132
foreach(_component ${ARG_COMPONENTS})
96133
set(_component_dir "${THEROCK_BINARY_DIR}/artifacts/${slice_name}_${_component}${_bundle_suffix}")
97134
set(_manifest_file "${_component_dir}/artifact_manifest.txt")
98135
set(_archive_file "${THEROCK_BINARY_DIR}/artifacts/${slice_name}_${_component}${_bundle_suffix}${THEROCK_ARTIFACT_ARCHIVE_SUFFIX}.tar.xz")
99136
list(APPEND _archive_files "${_archive_file}")
100137
set(_archive_sha_file "${_archive_file}.sha256sum")
138+
list(APPEND _archive_sha_files "${_archive_sha_file}")
101139
# TODO(#726): Lower compression levels are much faster for development and CI.
102140
# Set back to 6+ for production builds?
103141
set(_archive_compression_level 2)
@@ -117,39 +155,37 @@ function(therock_provide_artifact slice_name)
117155
"${_fileset_tool}"
118156
)
119157
endforeach()
120-
121158
add_custom_target("${_archive_target_name}" DEPENDS ${_archive_files})
122159
add_dependencies(therock-archives "${_archive_target_name}")
123-
endfunction()
124-
125160

126-
function(therock_create_dist)
127-
# Currently there is only one dist se we hard-code. These could become
128-
# settings later.
129-
set(_dist_dir "${THEROCK_BINARY_DIR}/dist/rocm")
130-
set(_stamp_file "${THEROCK_BINARY_DIR}/dist/.rocm.stamp")
131-
set(_dist_name "rocm")
132-
get_property(_artifact_dirs GLOBAL PROPERTY THEROCK_DIST_ARTIFACT_DIRS)
133-
134-
set(_fileset_tool "${THEROCK_SOURCE_DIR}/build_tools/fileset_tool.py")
135-
list(TRANSFORM _artifact_dirs APPEND "/artifact_manifest.txt" OUTPUT_VARIABLE _manifest_files)
136-
137-
add_custom_command(
138-
OUTPUT "${_stamp_file}"
139-
COMMENT "Creating dist ${_dist_dir}"
140-
COMMAND "${Python3_EXECUTABLE}" "${_fileset_tool}" artifact-flatten --verbose
141-
-o "${_dist_dir}" ${_artifact_dirs}
161+
# Archive expunge target.
162+
add_custom_target(
163+
"${_archive_target_name}+expunge"
142164
COMMAND
143-
"${CMAKE_COMMAND}" -E touch "${_stamp_file}"
144-
DEPENDS
145-
"${_fileset_tool}"
146-
${_manifest_files}
165+
"${CMAKE_COMMAND}" -E rm -f ${_archive_files} ${_archive_sha_files}
147166
)
167+
add_dependencies(therock-expunge "${_archive_target_name}+expunge")
148168

149-
set(_dist_target_name "therock-dist-${_dist_name}")
150-
add_custom_target("${_dist_target_name}" ALL DEPENDS "${_stamp_file}")
151-
if(NOT TARGET therock-dist)
152-
add_custom_target(therock-dist ALL)
169+
# Generate expunge targets.
170+
add_custom_target(
171+
"${_target_name}+expunge"
172+
COMMAND
173+
"${CMAKE_COMMAND}" -E rm -rf ${_component_dirs}
174+
)
175+
add_dependencies(therock-expunge "${_target_name}+expunge")
176+
177+
# For each subproject dep, we add a dependency on its +dist target to also
178+
# trigger overall artifact construction. In this way `ninja myfoo+dist`
179+
# will always populate all related artifacts and distributions. Note that
180+
# this only applies to the convenience +dist target, not the underlying
181+
# stamp-file chain, which is what the core dependency mechanism uses.
182+
if(ARG_DISTRIBUTION)
183+
foreach(subproject_dep ${ARG_SUBPROJECT_DEPS})
184+
set(_subproject_dist_target "${subproject_dep}+dist")
185+
if(NOT TARGET "${_subproject_dist_target}")
186+
message(FATAL_ERROR "Subproject convenience target ${_subproject_dist_target} does not exist")
187+
endif()
188+
add_dependencies("${_subproject_dist_target}" "${_target_name}")
189+
endforeach()
153190
endif()
154-
add_dependencies(therock-dist "${_dist_target_name}")
155191
endfunction()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Various top level targets that have dependencies added to them build system
2+
# wide. In general, the canonical name for all such targets is prefixed with
3+
# "therock-{name}", but also bare name targets are defined if not already.
4+
5+
function(therock_add_convenience_target name)
6+
add_custom_target("therock-${name}")
7+
if(NOT TARGET "${name}")
8+
add_custom_target("${name}" DEPENDS "therock-${name}")
9+
endif()
10+
endfunction()
11+
12+
function(therock_add_convenience_target_all name)
13+
add_custom_target("therock-${name}" ALL)
14+
if(NOT TARGET "${name}")
15+
add_custom_target("${name}" DEPENDS "therock-${name}")
16+
endif()
17+
endfunction()
18+
19+
# Populates all artifacts and distributions (i.e. all artifact-foo targets).
20+
therock_add_convenience_target(artifacts)
21+
# Builds archives for all artifacts (i.e. all archive-foo targets).
22+
therock_add_convenience_target(archives)
23+
# Populates all distribution directories (i.e. all dist-foo targets).
24+
therock_add_convenience_target(dist)
25+
# Expunges configure/build byproducts and artifacts for all projects.
26+
therock_add_convenience_target(expunge)

cmake/therock_global_post_subproject.cmake

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,33 @@ if(THEROCK_SPLIT_DEBUG_INFO AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
116116
)
117117
endblock()
118118
endif()
119+
120+
# Add convenience targets within the sub-project that interact with the super-project.
121+
# This allows developers to work entirely in the sub-project build directory and
122+
# perform most operations.
123+
124+
# Removes the sub-project stage.stamp file. This will cause any subsequent action
125+
# on the super-project to see the sub-project as out of date with respect to
126+
# being stage installed and populated in distributions. We invoke it on ALL
127+
# so that most local build commands (i.e. `ninja` without args) triggers
128+
# invalidation. During normal super-project build, once the build phase is
129+
# done, the build.stamp will be touched, which will cause the stage.stamp to
130+
# show as out of date. So either way, the ordering is preserved.
131+
add_custom_target(therock-touch ALL
132+
COMMAND
133+
"${CMAKE_COMMAND}" -E rm -f "${THEROCK_STAGE_STAMP_FILE}"
134+
VERBATIM
135+
)
136+
137+
# Removes the sub-project build.stamp file, indicating that the project must be
138+
# rebuilt, then invokes the parent {target}+dist, causing all stage installation
139+
# and distributions to be populated upon successful build.
140+
add_custom_target(therock-dist
141+
COMMAND
142+
"${CMAKE_COMMAND}" -E rm -f "${THEROCK_BUILD_STAMP_FILE}"
143+
COMMAND
144+
"${CMAKE_COMMAND}" --build "${THEROCK_BINARY_DIR}" --target "${THEROCK_SUBPROJECT_TARGET}+dist"
145+
COMMENT "Trigger super-project dist"
146+
VERBATIM
147+
USES_TERMINAL
148+
)

0 commit comments

Comments
 (0)