Skip to content

Commit 9a7732e

Browse files
peterbell10facebook-github-bot
authored andcommitted
CMake: Support dynamic codegen outputs (pytorch#68246)
Summary: Pull Request resolved: pytorch#68246 Currently the codegen produces a list of output files at CMake configuration time and the build system has no way of knowing if the outputs change. So if that happens, you basically need to delete the build folder and re-run from scratch. Instead, this generates the output list every time the code generation is run and changes the output to be a `.cmake` file that gets included in the main cmake configuration step. That means the build system knows to re-run cmake automatically if a new output is added. So, for example you could change the number of shards that `Operators.cpp` is split into and it all just works transparently to the user. Test Plan: Imported from OSS Reviewed By: zou3519 Differential Revision: D32596268 Pulled By: albanD fbshipit-source-id: 15e0896aeaead90aed64b9c8fda70cf28fef13a2
1 parent cd9da32 commit 9a7732e

File tree

3 files changed

+54
-47
lines changed

3 files changed

+54
-47
lines changed

cmake/Codegen.cmake

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,31 @@ if(INTERN_BUILD_ATEN_OPS)
201201
${GEN_VULKAN_FLAGS}
202202
)
203203

204+
file(GLOB_RECURSE headers_templates "${CMAKE_CURRENT_LIST_DIR}/../aten/src/ATen/templates/*\.h")
205+
file(GLOB_RECURSE sources_templates "${CMAKE_CURRENT_LIST_DIR}/../aten/src/ATen/templates/*\.cpp")
206+
set(declarations_yaml_templates "")
207+
208+
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/aten/src/ATen)
209+
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/aten/src/ATen/core)
210+
204211
foreach(gen_type "headers" "sources" "declarations_yaml")
212+
# The codegen outputs may change dynamically as PyTorch is
213+
# developed, but add_custom_command only supports dynamic inputs.
214+
#
215+
# We work around this by generating a .cmake file which is
216+
# included below to set the list of output files. If that file
217+
# ever changes then cmake will be re-run automatically because it
218+
# was included and so we get fully dynamic outputs.
219+
220+
set("GEN_COMMAND_${gen_type}"
221+
${GEN_COMMAND}
222+
--generate ${gen_type}
223+
--output-dependencies ${CMAKE_BINARY_DIR}/aten/src/ATen/generated_${gen_type}.cmake
224+
)
225+
226+
# Dry run to bootstrap the output variables
205227
execute_process(
206-
COMMAND ${GEN_COMMAND}
207-
--generate ${gen_type}
208-
--output-dependencies ${CMAKE_BINARY_DIR}/aten/src/ATen/generated_${gen_type}.txt
228+
COMMAND ${GEN_COMMAND_${gen_type}} --dry-run
209229
RESULT_VARIABLE RETURN_VALUE
210230
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
211231
)
@@ -214,43 +234,22 @@ if(INTERN_BUILD_ATEN_OPS)
214234
message(FATAL_ERROR "Failed to get generated_${gen_type} list")
215235
endif()
216236

217-
file(READ "${CMAKE_BINARY_DIR}/aten/src/ATen/generated_${gen_type}.txt" "generated_${gen_type}")
218-
file(READ "${CMAKE_BINARY_DIR}/aten/src/ATen/generated_${gen_type}.txt-cuda" "cuda_generated_${gen_type}")
219-
file(READ "${CMAKE_BINARY_DIR}/aten/src/ATen/generated_${gen_type}.txt-core" "core_generated_${gen_type}")
220-
endforeach()
221-
222-
file(GLOB_RECURSE header_templates "${CMAKE_CURRENT_LIST_DIR}/../aten/src/ATen/templates/*\.h")
223-
file(GLOB_RECURSE source_templates "${CMAKE_CURRENT_LIST_DIR}/../aten/src/ATen/templates/*\.cpp")
224-
225-
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/aten/src/ATen)
226-
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/aten/src/ATen/core)
227-
228-
add_custom_command(
229-
OUTPUT ${generated_headers} ${cuda_generated_headers} ${core_generated_headers}
230-
COMMAND ${GEN_COMMAND} --generate headers
231-
DEPENDS ${all_python} ${header_templates}
232-
${CMAKE_CURRENT_LIST_DIR}/../aten/src/ATen/native/native_functions.yaml
233-
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
234-
)
235-
236-
add_custom_command(
237-
OUTPUT ${generated_sources} ${cuda_generated_sources} ${core_generated_sources}
238-
COMMAND ${GEN_COMMAND} --generate sources
239-
DEPENDS ${all_python} ${source_templates}
240-
${CMAKE_CURRENT_LIST_DIR}/../aten/src/ATen/native/native_functions.yaml
241-
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
242-
)
243-
244-
add_custom_command(
245-
OUTPUT
246-
${generated_declarations_yaml}
247-
${cuda_generated_declarations_yaml}
248-
${core_generated_declarations_yaml}
249-
COMMAND ${GEN_COMMAND} --generate declarations_yaml
250-
DEPENDS ${all_python}
251-
${CMAKE_CURRENT_LIST_DIR}/../aten/src/ATen/native/native_functions.yaml
252-
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
237+
include("${CMAKE_BINARY_DIR}/aten/src/ATen/generated_${gen_type}.cmake")
238+
include("${CMAKE_BINARY_DIR}/aten/src/ATen/core_generated_${gen_type}.cmake")
239+
include("${CMAKE_BINARY_DIR}/aten/src/ATen/cuda_generated_${gen_type}.cmake")
240+
241+
add_custom_command(
242+
COMMENT "Generating ATen ${gen_type}"
243+
OUTPUT ${generated_${gen_type}} ${cuda_generated_${gen_type}} ${core_generated_${gen_type}}
244+
${CMAKE_BINARY_DIR}/aten/src/ATen/generated_${gen_type}.cmake
245+
${CMAKE_BINARY_DIR}/aten/src/ATen/core_generated_${gen_type}.cmake
246+
${CMAKE_BINARY_DIR}/aten/src/ATen/cuda_generated_${gen_type}.cmake
247+
COMMAND ${GEN_COMMAND_${gen_type}}
248+
DEPENDS ${all_python} ${${gen_type}_templates}
249+
${CMAKE_CURRENT_LIST_DIR}/../aten/src/ATen/native/native_functions.yaml
250+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
253251
)
252+
endforeach()
254253

255254
# Generated headers used from a CUDA (.cu) file are
256255
# not tracked correctly in CMake. We make the libATen.so depend explicitly

tools/codegen/gen.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,9 @@ def main() -> None:
12001200
'-o',
12011201
'--output-dependencies',
12021202
help='output a list of dependencies into the given file and exit')
1203+
parser.add_argument(
1204+
'--dry-run', action='store_true',
1205+
help='run without writing any files (still updates outputs)')
12031206
parser.add_argument(
12041207
'-d', '--install_dir', help='output directory',
12051208
default='build/aten/src/ATen')
@@ -1271,7 +1274,7 @@ def main() -> None:
12711274
pathlib.Path(core_install_dir).mkdir(parents=True, exist_ok=True)
12721275

12731276
def make_file_manager(install_dir: str) -> FileManager:
1274-
return FileManager(install_dir=install_dir, template_dir=template_dir, dry_run=options.output_dependencies)
1277+
return FileManager(install_dir=install_dir, template_dir=template_dir, dry_run=options.dry_run)
12751278

12761279
core_fm = make_file_manager(core_install_dir)
12771280
cpu_fm = make_file_manager(options.install_dir)
@@ -1356,9 +1359,14 @@ def make_file_manager(install_dir: str) -> FileManager:
13561359
cpu_fm=cpu_fm)
13571360

13581361
if options.output_dependencies:
1359-
cpu_fm.write_outputs(options.output_dependencies)
1360-
core_fm.write_outputs(f"{options.output_dependencies}-core")
1361-
cuda_fm.write_outputs(f"{options.output_dependencies}-cuda")
1362+
depfile_path = pathlib.Path(options.output_dependencies).resolve()
1363+
depfile_name = depfile_path.name
1364+
depfile_stem = depfile_path.stem
1365+
1366+
for fm, prefix in [(cpu_fm, ""), (core_fm, "core_"), (cuda_fm, "cuda_")]:
1367+
varname = prefix + depfile_stem
1368+
path = depfile_path.parent / (prefix + depfile_name)
1369+
fm.write_outputs(varname, str(path))
13621370

13631371

13641372
if __name__ == '__main__':

tools/codegen/utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,9 @@ def merge_env(into: Dict[str, List[str]], from_: Dict[str, List[str]]) -> None:
224224
self.filenames.discard(
225225
f"{self.install_dir}/{base_filename}Everything{extension}")
226226

227-
def write_outputs(self, filename: str) -> None:
227+
def write_outputs(self, variable_name: str, filename: str) -> None:
228228
"""Write a file containing the list of all outputs which are
229229
generated by this script."""
230-
self._write_if_changed(
231-
filename,
232-
''.join(name + ";" for name in sorted(self.filenames)))
230+
content = 'set({}\n {})'.format(
231+
variable_name, '\n '.join('"' + name + '"' for name in sorted(self.filenames)))
232+
self._write_if_changed(filename, content)

0 commit comments

Comments
 (0)