Skip to content

Commit 71d4769

Browse files
dont force nodes folder to exist
1 parent c1a946d commit 71d4769

2 files changed

Lines changed: 62 additions & 39 deletions

File tree

README.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -383,21 +383,23 @@ When you call `jig_auto_package()`, it:
383383
7. **Installs everything** to proper locations (headers, libraries, executables, Python packages)
384384
8. **Auto-installs common directories** like `launch/` and `config/` if they exist
385385

386+
If `nodes/` is absent, steps 1–7 are skipped and `jig_auto_package()` behaves as a thin wrapper around `ament_auto_package()` — useful for launch-only packages, config-only packages, shared-interface packages, and metapackages. See [Packages Without Nodes](#packages-without-nodes).
387+
386388
#### Directory Convention
387389

388390
```
389391
my_package/
390-
├── nodes/ # Required: All nodes go here
392+
├── nodes/ # All nodes go here
391393
│ ├── my_cpp_node/
392-
│ │ ├── interface.yaml # Required
394+
│ │ ├── interface.yaml # Per-node interface definition
393395
│ │ └── my_cpp_node.hpp # Implementation
394396
│ │ └── my_cpp_node.cpp # Implementation
395397
│ └── my_py_node/
396-
│ ├── interface.yaml # Required
398+
│ ├── interface.yaml # Per-node interface definition
397399
│ └── my_py_node.py # Implementation
398-
├── launch/ # Optional: Auto-installed if exists
399-
├── config/ # Optional: Auto-installed if exists
400-
├── interfaces/ # Optional: Package-level interface definitions
400+
├── launch/ # Auto-installed if exists
401+
├── config/ # Auto-installed if exists
402+
├── interfaces/ # Package-level interface definitions
401403
├── CMakeLists.txt
402404
└── package.xml
403405
```
@@ -425,6 +427,25 @@ my_package/
425427
426428
All nodes will be built and registered automatically.
427429
430+
#### Packages Without Nodes
431+
432+
`nodes/` is optional, so the same `jig_auto_package()` macro works for packages that don't ship any node implementations:
433+
434+
- **Launch-only packages** containing `launch/` files that bring up nodes from elsewhere.
435+
- **Config-only packages** that just install YAML configs, RViz layouts, maps, URDFs, etc.
436+
- **Shared-interface packages** that publish reusable `interface.yaml` files via a top-level `interfaces/` directory.
437+
- **Metapackages** that exist purely to declare dependencies in `package.xml`.
438+
439+
```
440+
my_launch_pkg/
441+
├── launch/ # Auto-installed
442+
├── config/ # Auto-installed
443+
├── CMakeLists.txt # find_package(jig REQUIRED); jig_auto_package()
444+
└── package.xml
445+
```
446+
447+
When `nodes/` is missing, all per-node code generation, library creation, and component registration steps are skipped — the macro just installs `launch/`, `config/`, anything in `INSTALL_TO_SHARE`, and any top-level `interfaces/*.yaml`, then calls `ament_auto_package()`.
448+
428449
#### Install Additional Directories
429450
430451
```cmake

jig/cmake/jig_auto_package.cmake

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# ============================================================================
22
# jig_auto_package.cmake
33
#
4-
# Automated build system for ROS 2 packages with jig nodes. Expects nodes/ directory with subdirectories containing
5-
# interface.yaml and C++ or Python implementation files.
4+
# Automated build system for ROS 2 packages with jig nodes. Optionally expects a nodes/ directory with subdirectories
5+
# containing interface.yaml and C++ or Python implementation files; packages without nodes/ are also supported (e.g.
6+
# launch-only, config-only, or shared-interface metapackages).
67
#
78
# NOTE: Most internal functions are implemented as macros (not functions) for consistency with ament_cmake patterns and
89
# to preserve variable scope behavior. This allows macros like ament_auto_add_library() and
@@ -47,8 +48,8 @@ function(_jig_snake_to_pascal OUTPUT_VAR INPUT_STRING)
4748
endfunction()
4849

4950
# Main entry point for jig build system Automates the entire build process for ROS 2 packages with jig nodes. Detects
50-
# languages, generates interfaces, builds libraries, and registers components. Requires: nodes/ directory with at least
51-
# one .cpp or .py file
51+
# languages, generates interfaces, builds libraries, and registers components. If nodes/ is absent, all per-node steps
52+
# are skipped and only top-level interfaces/, launch/, config/, and INSTALL_TO_SHARE assets are installed.
5253
macro(jig_auto_package)
5354
# Parse optional arguments
5455
set(_jig_auto_options "")
@@ -79,31 +80,30 @@ macro(jig_auto_package)
7980
# NOTE: JIG_NODES_DIR is part of the jig cmake API
8081
set(JIG_NODES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/nodes")
8182

82-
if(NOT IS_DIRECTORY ${JIG_NODES_DIR})
83-
message(FATAL_ERROR "jig: nodes/ directory not found at ${JIG_NODES_DIR}")
84-
endif()
83+
# nodes/ is optional: a jig package may exist purely for launch files, configs, or shared interface YAMLs.
84+
if(IS_DIRECTORY ${JIG_NODES_DIR})
85+
_jig_detect_languages(${JIG_NODES_DIR} _jig_HAS_CPP _jig_HAS_PYTHON)
8586

86-
_jig_detect_languages(${JIG_NODES_DIR} _jig_HAS_CPP _jig_HAS_PYTHON)
87+
if(NOT _jig_HAS_CPP AND NOT _jig_HAS_PYTHON)
88+
message(FATAL_ERROR "jig: nodes/ directory has no C++ (.cpp) or Python (.py) files.")
89+
endif()
8790

88-
if(NOT _jig_HAS_CPP AND NOT _jig_HAS_PYTHON)
89-
message(FATAL_ERROR "jig: nodes/ directory has no C++ (.cpp) or Python (.py) files.")
90-
endif()
91+
if(_jig_HAS_CPP)
92+
# NOTE: JIG_CPP_PACKAGE_TARGET is part of the jig cmake API
93+
set(JIG_CPP_PACKAGE_TARGET "${PROJECT_NAME}")
94+
_jig_create_package_shared_cpp_library(${JIG_CPP_PACKAGE_TARGET})
95+
endif()
9196

92-
if(_jig_HAS_CPP)
93-
# NOTE: JIG_CPP_PACKAGE_TARGET is part of the jig cmake API
94-
set(JIG_CPP_PACKAGE_TARGET "${PROJECT_NAME}")
95-
_jig_create_package_shared_cpp_library(${JIG_CPP_PACKAGE_TARGET})
96-
endif()
97+
if(_jig_HAS_PYTHON)
98+
# set up python package
99+
find_package(ament_cmake_python REQUIRED)
100+
_ament_cmake_python_get_python_install_dir()
101+
_jig_create_top_level_python_package()
102+
endif()
97103

98-
if(_jig_HAS_PYTHON)
99-
# set up python package
100-
find_package(ament_cmake_python REQUIRED)
101-
_ament_cmake_python_get_python_install_dir()
102-
_jig_create_top_level_python_package()
104+
_jig_generate_nodes(${JIG_NODES_DIR})
103105
endif()
104106

105-
_jig_generate_nodes(${JIG_NODES_DIR})
106-
107107
# Process and install interface.yaml files with token replacement
108108
_jig_process_and_install_interfaces(${JIG_NODES_DIR})
109109

@@ -417,18 +417,20 @@ macro(_jig_process_and_install_interfaces NODES_DIR)
417417
set(_jig_interfaces_output_dir "${CMAKE_CURRENT_BINARY_DIR}/interfaces")
418418
file(MAKE_DIRECTORY ${_jig_interfaces_output_dir})
419419

420-
# Collect expected generated node YAML names (for conflict checking)
420+
# Collect expected generated node YAML names (for conflict checking). Skipped when nodes/ is absent.
421421
set(_jig_generated_node_yaml_names "")
422-
file(GLOB _jig_interface_NODE_DIRS RELATIVE ${NODES_DIR} ${NODES_DIR}/*)
423-
foreach(_jig_interface_NODE_ENTRY ${_jig_interface_NODE_DIRS})
424-
set(_jig_interface_NODE_PATH "${NODES_DIR}/${_jig_interface_NODE_ENTRY}")
425-
if(IS_DIRECTORY ${_jig_interface_NODE_PATH})
426-
set(_jig_interface_YAML_PATH "${_jig_interface_NODE_PATH}/interface.yaml")
427-
if(EXISTS ${_jig_interface_YAML_PATH})
428-
list(APPEND _jig_generated_node_yaml_names "${_jig_interface_NODE_ENTRY}.yaml")
422+
if(IS_DIRECTORY ${NODES_DIR})
423+
file(GLOB _jig_interface_NODE_DIRS RELATIVE ${NODES_DIR} ${NODES_DIR}/*)
424+
foreach(_jig_interface_NODE_ENTRY ${_jig_interface_NODE_DIRS})
425+
set(_jig_interface_NODE_PATH "${NODES_DIR}/${_jig_interface_NODE_ENTRY}")
426+
if(IS_DIRECTORY ${_jig_interface_NODE_PATH})
427+
set(_jig_interface_YAML_PATH "${_jig_interface_NODE_PATH}/interface.yaml")
428+
if(EXISTS ${_jig_interface_YAML_PATH})
429+
list(APPEND _jig_generated_node_yaml_names "${_jig_interface_NODE_ENTRY}.yaml")
430+
endif()
429431
endif()
430-
endif()
431-
endforeach()
432+
endforeach()
433+
endif()
432434

433435
# Process top-level interfaces/ directory if it exists
434436
set(_jig_toplevel_interfaces_dir "${CMAKE_CURRENT_SOURCE_DIR}/interfaces")

0 commit comments

Comments
 (0)