diff --git a/MANIFEST.in b/MANIFEST.in index 9f4e99bebd..679a29d47c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,9 @@ include versioneer.py include legate/_version.py include legate/py.typed +include legate/cpp_source_template +include legate/cpp_header_template +include legate/python_template # include interface files -recursive-include legate *.pyi \ No newline at end of file +recursive-include legate *.pyi diff --git a/README.md b/README.md index 87d001510c..03bddf2947 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,30 @@ Memory: Registered CPU-side pinned memory per rank (in MBs) : 0 ``` +## Getting Your Own Legate Project Started + + +### Hello World + +For basic code examples illustrating how Legate tasks are created +and enqueued, users can look at the [hello world](examples/hello/README.md) example. +The folder includes both a basic "Hello World" as well numerical +examples like variance showing both elementwise array operations +and distributed reductions. + +### Library Template + +To bootstrap your own Legate library, the `legate-create-library` script +can be used to generate both the build system and initial set of +C++ and Python files. + +```bash +$ legate-create-library myproject +$ ls myproject +CMakeLists.txt editable-install.sh install.sh myproject setup.py src +``` + + ## Other FAQs * *Does Legate only work on NVIDIA hardware?* @@ -465,7 +489,7 @@ See the discussion of contributing in [CONTRIBUTING.md](CONTRIBUTING.md). ## Documentation -A complete list of available features can is found in the [Legate Core +A complete list of available features can be found in the [Legate Core documentation](https://nv-legate.github.io/legate.core). ## Next Steps diff --git a/cmake/legate_helper_functions.cmake b/cmake/legate_helper_functions.cmake index de2216c37f..c92d0b3e86 100644 --- a/cmake/legate_helper_functions.cmake +++ b/cmake/legate_helper_functions.cmake @@ -278,99 +278,10 @@ function(legate_add_cpp_subdirectory dir) endfunction() function(legate_cpp_library_template target output_sources_variable) - set(file_template -[=[ -/* Copyright 2023 NVIDIA Corporation - * - * 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 - * - * http://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. - * - */ - -#pragma once - -#include "legate.h" - -namespace @target@ { - -struct Registry { - static legate::TaskRegistrar& get_registrar(); -}; - -template -struct Task : public legate::LegateTask { - using Registrar = Registry; - static constexpr int TASK_ID = ID; -}; - -} -]=]) - string(CONFIGURE "${file_template}" file_content @ONLY) + string(CONFIGURE "${Legate_CPP_HEADER_TEMPLATE}" file_content @ONLY) file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/legate_library.h "${file_content}") - set(file_template -[=[ -/* Copyright 2023 NVIDIA Corporation - * - * 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 - * - * http://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 "legate_library.h" - -namespace @target@ { - -static const char* const library_name = "@target@"; - -Legion::Logger log_@target@(library_name); - -/*static*/ legate::TaskRegistrar& Registry::get_registrar() -{ - static legate::TaskRegistrar registrar; - return registrar; -} - -void registration_callback() -{ - auto context = legate::Runtime::get_runtime()->create_library(library_name); - - Registry::get_registrar().register_all_tasks(context); -} - -} // namespace @target@ - -extern "C" { - -void @target@_perform_registration(void) -{ - // Tell the runtime about our registration callback so we hook it - // in before the runtime starts and make it global so that we know - // that this call back is invoked everywhere across all nodes - legate::Core::perform_registration<@target@::registration_callback>(); -} - -} -]=]) - string(CONFIGURE "${file_template}" file_content @ONLY) + string(CONFIGURE "${Legate_CPP_SOURCE_TEMPLATE}" file_content @ONLY) file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/legate_library.cc "${file_content}") set(${output_sources_variable} @@ -381,88 +292,31 @@ void @target@_perform_registration(void) endfunction() function(legate_python_library_template py_path) -set(options) -set(one_value_args TARGET PY_IMPORT_PATH) -set(multi_value_args) -cmake_parse_arguments( - LEGATE_OPT - "${options}" - "${one_value_args}" - "${multi_value_args}" - ${ARGN} -) - -if (DEFINED LEGATE_OPT_TARGET) - set(target "${LEGATE_OPT_TARGET}") -else() - string(REPLACE "/" "_" target "${py_path}") -endif() - -if (DEFINED LEGATE_OPT_PY_IMPORT_PATH) - set(py_import_path "${LEGATE_OPT_PY_IMPORT_PATH}") -else() - string(REPLACE "/" "." py_import_path "${py_path}") -endif() - -set(fn_library "${CMAKE_CURRENT_SOURCE_DIR}/${py_path}/library.py") - -set(file_template -[=[ -# Copyright 2023 NVIDIA Corporation -# -# 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 -# -# http://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. -# - -from legate.core import ( - Library, - get_legate_runtime, -) -import os -from typing import Any - -class UserLibrary(Library): - def __init__(self, name: str) -> None: - self.name = name - self.shared_object: Any = None - - @property - def cffi(self) -> Any: - return self.shared_object - - def get_name(self) -> str: - return self.name - - def get_shared_library(self) -> str: - from @py_import_path@.install_info import libpath - return os.path.join(libpath, f"lib@target@{self.get_library_extension()}") - - def get_c_header(self) -> str: - from @py_import_path@.install_info import header - - return header + set(options) + set(one_value_args TARGET PY_IMPORT_PATH) + set(multi_value_args) + cmake_parse_arguments( + LEGATE_OPT + "${options}" + "${one_value_args}" + "${multi_value_args}" + ${ARGN} + ) - def get_registration_callback(self) -> str: - return "@target@_perform_registration" + if (DEFINED LEGATE_OPT_TARGET) + set(target "${LEGATE_OPT_TARGET}") + else() + string(REPLACE "/" "_" target "${py_path}") + endif() - def initialize(self, shared_object: Any) -> None: - self.shared_object = shared_object + if (DEFINED LEGATE_OPT_PY_IMPORT_PATH) + set(py_import_path "${LEGATE_OPT_PY_IMPORT_PATH}") + else() + string(REPLACE "/" "." py_import_path "${py_path}") + endif() - def destroy(self) -> None: - pass + set(fn_library "${CMAKE_CURRENT_SOURCE_DIR}/${py_path}/library.py") -user_lib = UserLibrary("@target@") -user_context = get_legate_runtime().register_library(user_lib) -]=]) - string(CONFIGURE "${file_template}" file_content @ONLY) + string(CONFIGURE "${Legate_PYTHON_TEMPLATE}" file_content @ONLY) file(WRITE "${fn_library}" "${file_content}") endfunction() diff --git a/examples/io/src/read_file.cc b/examples/io/src/read_file.cc index 9d2c3f5601..fb96a71324 100644 --- a/examples/io/src/read_file.cc +++ b/examples/io/src/read_file.cc @@ -48,7 +48,8 @@ struct read_fn { // Compute the absolute offsets to the section that this reader task // is supposed to read from the file int64_t my_lo = my_id * size / num_readers; - int64_t my_hi = std::min((my_id + 1) * size / num_readers, size); + // force min comparison in int64_t + int64_t my_hi = std::min((my_id + 1) * size / num_readers, size); // Then compute the extent for the output and create the output buffer to populate int64_t my_ext = my_hi - my_lo; diff --git a/legate-create-library b/legate-create-library new file mode 100755 index 0000000000..8f4906a5c1 --- /dev/null +++ b/legate-create-library @@ -0,0 +1,229 @@ +#! /usr/bin/env python + +from __future__ import annotations + +import argparse +import importlib.resources +import os +import stat +import sys +from pathlib import Path +from typing import Union + +parser = argparse.ArgumentParser() + +parser.add_argument("name", type=str, help="The name of the Legate library") + +args, _ = parser.parse_known_args() + + +libname = args.name +if not libname.isidentifier(): + sys.exit(f"Name '{libname}' is not valid, must be a C++/Python name") + +package_data_root = importlib.resources.files("legate") + + +def get_template(relpath): + with open(package_data_root / relpath) as f: + return f.read() + + +cpp_source_template = get_template("cpp_source_template") +cpp_header_template = get_template("cpp_header_template") +python_template = get_template("python_template") + +cmake_toplevel_template = """ +#============================================================================= +# Copyright 2023 NVIDIA Corporation +# +# 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 +# +# http://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. +#============================================================================= + +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) + +project(@target@ VERSION 1.0 LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 17) +set(BUILD_SHARED_LIBS ON) + +find_package(legate_core REQUIRED) + +legate_add_cpp_subdirectory(src TARGET @target@ EXPORT @target@-export) + +legate_add_cffi(${CMAKE_CURRENT_SOURCE_DIR}/src/@target@_cffi.h TARGET @target@) +legate_default_python_install(@target@ EXPORT @target@-export) +""" # noqa: E501 + +cmake_src_template = """ +#============================================================================= +# Copyright 2023 NVIDIA Corporation +# +# 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 +# +# http://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. +#============================================================================= + +add_library( + @target@ + legate_library.h + legate_library.cc +) + +target_include_directories(@target@ + PRIVATE + $ + INTERFACE + $ +) + +target_link_libraries(@target@ PRIVATE legate::core) +""" + +setup_template = """ +#!/usr/bin/env python3 + +# Copyright 2023 NVIDIA Corporation +# +# 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 +# +# http://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. +# +import os +from pathlib import Path + +from setuptools import find_packages +from skbuild import setup + +import legate.install_info as lg_install_info + +legate_dir = Path(lg_install_info.libpath).parent.as_posix() + +cmake_flags = [ + f"-Dlegate_core_ROOT:STRING={legate_dir}", +] + +env_cmake_args = os.environ.get("CMAKE_ARGS") +if env_cmake_args is not None: + cmake_flags.append(env_cmake_args) +os.environ["CMAKE_ARGS"] = " ".join(cmake_flags) + + +setup( + name="Legate @target@", + version="0.1", + description="@target@ for Legate", + author="NVIDIA Corporation", + license="Apache 2.0", + classifiers=[ + "Intended Audience :: Developers", + "Topic :: Database", + "Topic :: Scientific/Engineering", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + ], + packages=find_packages( + where=".", + include=["@target@", "@target@.*"], + ), + include_package_data=True, + zip_safe=False, +) +""" + +editable_script = """ +legate_root=`python -c 'import legate.install_info as i; from pathlib import Path; print(Path(i.libpath).parent.resolve())'` +echo "Using Legate at $legate_root" +cmake -S . -B build -D legate_core_ROOT=$legate_root +cmake --build build +python -m pip install -e . -vv +""" # noqa: E501 + +install_script = """ +python -m pip install . +""" + + +def generate_file( + libname: str, + template: str, + path: Union[Path, str], + executable: bool = False, +): + target_path = Path(libname) / Path(path) + if not target_path.parent.is_dir(): + target_path.parent.mkdir(parents=True) + + # Use CMake @ONLY for template substitution instead + # of string.Template so that CMake and Python + # share a common set of templates. + text = template.replace("@target@", libname) + text = text.replace("@py_import_path@", libname) + with open(target_path, "w") as f: + f.write(text) + + if executable: + st = os.stat(target_path) + os.chmod(target_path, st.st_mode | stat.S_IRWXU) + + +cffi_template = """ +/* Copyright 2023 NVIDIA Corporation + * + * 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 + * + * http://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. + * + */ + +enum OpCode { +}; +""" + +generate_file(libname, cpp_source_template, "src/legate_library.cc") +generate_file(libname, cpp_header_template, "src/legate_library.h") +generate_file(libname, python_template, Path(libname) / f"{libname}.py") +generate_file(libname, "", Path(libname) / "__init__.py") +generate_file(libname, setup_template, "setup.py") +generate_file(libname, editable_script, "editable-install.sh", executable=True) +generate_file(libname, install_script, "install.sh", executable=True) +generate_file(libname, cmake_toplevel_template, "CMakeLists.txt") +generate_file(libname, cmake_src_template, "src/CMakeLists.txt") +generate_file(libname, cffi_template, f"src/{libname}_cffi.h") diff --git a/legate/cpp_header_template b/legate/cpp_header_template new file mode 100644 index 0000000000..657760b866 --- /dev/null +++ b/legate/cpp_header_template @@ -0,0 +1,34 @@ +/* Copyright 2023 NVIDIA Corporation + * + * 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 + * + * http://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. + * + */ + +#pragma once + +#include "legate.h" + +namespace @target@ { + +struct Registry { + public: + static legate::TaskRegistrar& get_registrar(); +}; + +template +struct Task : public legate::LegateTask { + using Registrar = Registry; + static constexpr int TASK_ID = ID; +}; + +} diff --git a/legate/cpp_source_template b/legate/cpp_source_template new file mode 100644 index 0000000000..d51e1116a6 --- /dev/null +++ b/legate/cpp_source_template @@ -0,0 +1,50 @@ +/* Copyright 2023 NVIDIA Corporation + * + * 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 + * + * http://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 "legate_library.h" + +namespace @target@ { + +static const char* const library_name = "@target@"; + +Legion::Logger log_@target@(library_name); + +/*static*/ legate::TaskRegistrar& Registry::get_registrar() +{ + static legate::TaskRegistrar registrar; + return registrar; +} + +void registration_callback() +{ + auto context = legate::Runtime::get_runtime()->create_library(library_name); + + Registry::get_registrar().register_all_tasks(context); +} + +} // namespace @target@ + +extern "C" { + +void @target@_perform_registration(void) +{ + // Tell the runtime about our registration callback so we hook it + // in before the runtime starts and make it global so that we know + // that this call back is invoked everywhere across all nodes + legate::Core::perform_registration<@target@::registration_callback>(); +} + +} diff --git a/legate/python_template b/legate/python_template new file mode 100644 index 0000000000..f8602a74c8 --- /dev/null +++ b/legate/python_template @@ -0,0 +1,54 @@ +# Copyright 2023 NVIDIA Corporation +# +# 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 +# +# http://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. +# + +from legate.core import ( + Library, + get_legate_runtime, +) +import os +from typing import Any + +class UserLibrary(Library): + def __init__(self, name: str) -> None: + self.name = name + self.shared_object: Any = None + + @property + def cffi(self) -> Any: + return self.shared_object + + def get_name(self) -> str: + return self.name + + def get_shared_library(self) -> str: + from @py_import_path@.install_info import libpath + return os.path.join(libpath, f"lib@target@{self.get_library_extension()}") + + def get_c_header(self) -> str: + from @target@.install_info import header + + return header + + def get_registration_callback(self) -> str: + return "@target@_perform_registration" + + def initialize(self, shared_object: Any) -> None: + self.shared_object = shared_object + + def destroy(self) -> None: + pass + +user_lib = UserLibrary("@target@") +user_context = get_legate_runtime().register_library(user_lib) diff --git a/legate_core_cpp.cmake b/legate_core_cpp.cmake index 4ec1b29086..f205f7f6fb 100644 --- a/legate_core_cpp.cmake +++ b/legate_core_cpp.cmake @@ -473,6 +473,9 @@ Imported Targets: ]=]) file(READ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/legate_helper_functions.cmake helper_functions) +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/legate/cpp_source_template cpp_source_template) +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/legate/cpp_header_template cpp_header_template) +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/legate/python_template python_template) string(JOIN "\n" code_string [=[ @@ -490,6 +493,15 @@ if(Legion_NETWORKS) find_package(MPI REQUIRED COMPONENTS CXX) endif() ]=] +"set(Legate_CPP_HEADER_TEMPLATE [=[" +"${cpp_header_template}" +"]=])" +"set(Legate_CPP_SOURCE_TEMPLATE [=[" +"${cpp_source_template}" +"]=])" +"set(Legate_PYTHON_TEMPLATE [=[" +"${python_template}" +"]=])" "${helper_functions}" ) diff --git a/setup.py b/setup.py index 83912f31f8..0403d40564 100755 --- a/setup.py +++ b/setup.py @@ -67,7 +67,7 @@ "lgpatch = legate.lgpatch:main", ], }, - scripts=["bind.sh"], + scripts=["bind.sh", "legate-create-library"], cmdclass=versioneer.get_cmdclass(), install_requires=["numpy>=1.22"], zip_safe=False,