diff --git a/Makefile.base b/Makefile.base index ab07fc5c9459..00d7eb059d8b 100644 --- a/Makefile.base +++ b/Makefile.base @@ -104,20 +104,30 @@ ASSMOBJ := $(ASSMSRC:%.S=$(BINDIR)/$(MODULE)/%.o) OBJ := $(OBJC) $(OBJCXX) $(ASMOBJ) $(ASSMOBJ) $(GENOBJC) DEP := $(OBJC:.o=.d) $(OBJCXX:.o=.d) $(ASSMOBJ:.o=.d) +SRC_ALL := $(SRC) $(SRCXX) $(ASMSRC) $(ASSMSRC) +SUBDIRS_IN_DIRS := $(filter $(DIRS), $(abspath $(sort $(dir $(SRC_ALL))))) +ifneq (,$(SUBDIRS_IN_DIRS)) + $(warning Files of the following subdirectories are selected \ + both as RIOT modules (using DIRS) and directly as sourcefiles (using SRC): \ + $(patsubst $(CURDIR)/%,./%, $(SUBDIRS_IN_DIRS)). \ + Please select a single approach for each subfolder to prevent linking errors.) +endif +SUBDIRS := $(filter-out $(BINDIR)/$(MODULE)/, $(dir $(OBJ))) + include $(RIOTMAKE)/blob.inc.mk include $(RIOTMAKE)/tools/fixdep.inc.mk -$(BINDIR)/$(MODULE)/: +$(BINDIR)/$(MODULE)/ $(SUBDIRS): $(Q)mkdir -p $@ -OLD_OBJECTS = $(wildcard $(BINDIR)/$(MODULE)/*.o) +OLD_OBJECTS = $(wildcard $(BINDIR)/$(MODULE)/*.o $(BINDIR)/$(MODULE)/**/*.o) # do not clean objects from bindist modules ifeq (,$(filter $(MODULE),$(BIN_USEMODULE))) OBJECTS_TO_REMOVE = $(filter-out $(OBJ),$(OLD_OBJECTS)) endif -$(MODULE).module compile-commands $(OBJ): | $(BINDIR)/$(MODULE)/ +$(MODULE).module compile-commands $(OBJ): | $(BINDIR)/$(MODULE)/ $(SUBDIRS) $(MODULE).module: $(OBJ) $(if $(OBJECTS_TO_REMOVE),$(MODULE).cleanup) | $(DIRS:%=ALL--%) diff --git a/Makefile.include b/Makefile.include index 771f7cfd49bb..9a5f8292d485 100644 --- a/Makefile.include +++ b/Makefile.include @@ -720,6 +720,7 @@ COMPILE_COMMANDS_FLAGS ?= --clangd compile-commands: $(COMPILE_COMMANDS_PATH) %/compile_commands.json: $(BUILDDEPS) $(Q)DIRS="$(DIRS)" APPLICATION_BLOBS="$(BLOBS)" \ + APPLICATION_SRC="$(SRC)" APPLICATION_SRCXX="$(SRCXX)" APPLICATION_ASMSRC="$(ASMSRC)" APPLICATION_ASSMSRC="$(ASSMSRC)" \ "$(MAKE)" -C $(APPDIR) -f $(RIOTMAKE)/application.inc.mk compile-commands $(Q)$(RIOTTOOLS)/compile_commands/compile_commands.py $(COMPILE_COMMANDS_FLAGS) $(BINDIR) \ > $@ @@ -744,6 +745,7 @@ $(ELFFILE): $(BASELIBS) $(ARCHIVES) $(LD_SCRIPTS) $(APPLICATION_MODULE).module: pkg-build $(BUILDDEPS) $(Q)DIRS="$(DIRS)" APPLICATION_BLOBS="$(BLOBS)" \ + APPLICATION_SRC="$(SRC)" APPLICATION_SRCXX="$(SRCXX)" APPLICATION_ASMSRC="$(ASMSRC)" APPLICATION_ASSMSRC="$(ASSMSRC)" \ "$(MAKE)" -C $(APPDIR) -f $(RIOTMAKE)/application.inc.mk $(APPLICATION_MODULE).module: FORCE diff --git a/doc/doxygen/src/creating-an-application.md b/doc/doxygen/src/creating-an-application.md index 0cdfd743869d..3d7f468448d9 100644 --- a/doc/doxygen/src/creating-an-application.md +++ b/doc/doxygen/src/creating-an-application.md @@ -197,6 +197,74 @@ inside a modules os boards directory. The RIOT build system has both `EXTERNAL_MODULE_DIRS` and `EXTERNAL_BOARD_DIRS` variables to specify directories that contain extra modules and extra boards. +## Using subfodlers in application + +Applications can contain subfolders. + +``` +├── apps +│   └── my_app +│   ├── app_dependant +│   │   └── file.c +│   ├── Makefile +│   └── main.c +└── RIOT +``` + +To add subfolders, `SRC` can be used. + +The `Makefile` in my_app will be : + +``` +APPLICATION = my_app +PROJECT_BASE ?= $(CURDIR)/../.. +RIOTBASE ?= $(PROJECT_BASE)/RIOT + +# Declare the main.c and all .c file in subfolders +# that doesn't need to be done with module +SRC += main.c +SRC += app_dependant/file.c + +include $(RIOTBASE)/Makefile.include +``` + +`SRC` allows creating subfolders without needing to create a module with a `Makefile` but can be combined with modules: + +``` +├── apps +│   └── my_app +| ├── my_app_module +| | ├── Makefile +│   │   └── module.c +│   ├── app_dependant +│   │   └── file.c +│   ├── Makefile +│   └── main.c +└── RIOT +``` + +my_app got one subfolder and one module called my_app_module. + +When we want to add modules, we have nothing to add in `SRC`; we only add `DIRS` and `USEMODULE`: + +``` +APPLICATION = my_app +PROJECT_BASE ?= $(CURDIR)/../.. +RIOTBASE ?= $(PROJECT_BASE)/RIOT + +# Declare directory where the module is and +# usage of it. +DIRS += my_app_module +USEMODULE += my_app_module + +# Declare the main.c and all .c file in subfolders, +# which doesn't need to be done with modules. +SRC += main.c +SRC += app_dependant/file.c + +include $(RIOTBASE)/Makefile.include +``` + ## External Boards External boards can be ported in an identical way as porting a regular board to diff --git a/examples/subfolders/Makefile b/examples/subfolders/Makefile new file mode 100644 index 000000000000..b5a8e8870687 --- /dev/null +++ b/examples/subfolders/Makefile @@ -0,0 +1,33 @@ +# name of your application +APPLICATION = subfolders + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Add subfolders as modules +DIRS += module +USEMODULE += my_module # name as defined in module/Makefile + +# Add source files in subfolders manually +SRC += main.c +SRC += folder/a.c folder/subfolder/b.c + +# Alternative method to add files in subfolders using wildcards +# SRC += $(wildcard *.c folder/*.c folder/**/*.c) + +# Adding subfolders both via SRC and DIRS will generate a warning +# and likely fail during linking +# DIRS += folder + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/subfolders/README.md b/examples/subfolders/README.md new file mode 100644 index 000000000000..14986afe579b --- /dev/null +++ b/examples/subfolders/README.md @@ -0,0 +1,85 @@ +# Application Example with Subfolders + +This example demonstrates the usage of subfolders in a RIOT application +show-casing two possible approaches: RIOT modules and simple subfolders. + +## Details + +Consider the following folder structure of this example. +The source files in `module` are incorporated as a RIOT module, +while the source files in `folder` are considered part of the application itself. + +``` +. +├── folder +│ ├── a.c +│ └── subfolder +│ └── b.c +├── main.c +├── Makefile +├── module +│ ├── a.c +│ ├── b.c +│ └── Makefile +└── README.md +``` + +### RIOT modules + +At a minimum, each module in RIOT requires a `Makefile` with the following content: + +```Makefile +MODULE := my_module + +include $(RIOTBASE)/Makefile.base +``` + +If `MODULE` is not specified, the name of the module's directory is automatically used, +leaving only the last line as minimal content. +It is important to note that module names have to be unique both among _all_ RIOT modules, +i.e., including the modules that are part of RIOT itself. + +If not manually specified via `SRC`, all source files which reside +directly in the module's directory are considered part of the module. +RIOT modules are also described in greater detail [in the documentation](https://doc.riot-os.org/creating-modules.html). + +Two lines need to be added to the application's Makefile in order to compile and use the module: + +```Makefile +DIRS += module +USEMODULE += my_module +``` + +The string added to `DIRS` has to match the directory name, +while the string added to `USEMODULE` has to match the module's name as defined above. + + +### Subfolders + +Compared to the module approach, no additional Makefile is needed in the subfolder. +The application's Makefile needs to add _all_ source files explicitly, +including the ones residing directly in the application directory: + +```Makefile +SRC += main.c +SRC += folder/a.c folder/subfolder/b.c +``` + +To avoid listing all source files individually, it is of course possible +to use normal GNU make functions such as `wildcard`: + +```Makefile +SRC += $(wildcard *.c folder/*.c folder/**/*.c) +``` + + +## Which approach should I use? + +In general, modules in RIOT are well-defined units of code that provide a set of features to your application. +If this matches your use-case, i.e., you have all your application tests separated into a subfolder, +RIOT modules are probably the best approach. +It is good practice to prefix all your application modules to avoid name clashes. + +If however you barely want to organize your files in a sensible folder structure, +but always require all source files to be part of your application, +the more straight-forward subfolder approach is probably the better pick. diff --git a/examples/subfolders/folder/a.c b/examples/subfolders/folder/a.c new file mode 100644 index 000000000000..7d5f66244f0c --- /dev/null +++ b/examples/subfolders/folder/a.c @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2023 TU Dresden + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include + +void folder_a(void) +{ + puts("./folder/a.c"); +} diff --git a/examples/subfolders/folder/subfolder/b.c b/examples/subfolders/folder/subfolder/b.c new file mode 100644 index 000000000000..5aace94347ba --- /dev/null +++ b/examples/subfolders/folder/subfolder/b.c @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2023 TU Dresden + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include + +void folder_b(void) +{ + puts("./folder/subfolder/b.c"); +} diff --git a/examples/subfolders/main.c b/examples/subfolders/main.c new file mode 100644 index 000000000000..f7a8cf54b3b8 --- /dev/null +++ b/examples/subfolders/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 TU Dresden + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Application showcasing the use of subfolders in RIOT applications + * + * @author Mikolai Gütschow + * + * @} + */ + +#include + +void module_a(void); +void module_b(void); +void folder_a(void); +void folder_b(void); + +int main(void) +{ + puts("./main.c"); + // call functions from RIOT module + module_a(); + module_b(); + // call functions from subfolder + folder_a(); + folder_b(); + + return 0; +} diff --git a/examples/subfolders/module/Makefile b/examples/subfolders/module/Makefile new file mode 100644 index 000000000000..831cf70a7d62 --- /dev/null +++ b/examples/subfolders/module/Makefile @@ -0,0 +1,3 @@ +MODULE := my_module + +include $(RIOTBASE)/Makefile.base diff --git a/examples/subfolders/module/a.c b/examples/subfolders/module/a.c new file mode 100644 index 000000000000..15a8f1ae6e07 --- /dev/null +++ b/examples/subfolders/module/a.c @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2023 TU Dresden + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include + +void module_a(void) +{ + puts("./module/a.c"); +} diff --git a/examples/subfolders/module/b.c b/examples/subfolders/module/b.c new file mode 100644 index 000000000000..32d98a7661a6 --- /dev/null +++ b/examples/subfolders/module/b.c @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2023 TU Dresden + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include + +void module_b(void) +{ + puts("./module/b.c"); +} diff --git a/makefiles/application.inc.mk b/makefiles/application.inc.mk index 0460b057c163..274ec11445f5 100644 --- a/makefiles/application.inc.mk +++ b/makefiles/application.inc.mk @@ -3,17 +3,20 @@ MODULE = $(APPLICATION_MODULE) DIRS += $(RIOTCPU)/$(CPU) $(BOARDDIR) DIRS += $(RIOTBASE)/core $(RIOTBASE)/core/lib $(RIOTBASE)/drivers $(RIOTBASE)/sys -# For regular modules, adding files to BLOBS to their Makefile is sufficient to -# create the corresponding headers. +# For regular modules, adding files to BLOBS, SRC, SRCXX, ASMSRC or ASSMSRC +# in their Makefile is sufficient to explicitely set the variables. # # Application modules are different, as they use this makefile to build, thus # application level variables are not available unless exported. # -# But exporting e.g., BLOBS, would pre-set the variable for all -# submakefiles. +# But exporting would pre-set the variables for all submakefiles. # -# As workaround, $(RIOTBASE)/Makefile.include passes BLOBS to this -# Makefile as APPLICATION_BLOBS. -BLOBS = $(APPLICATION_BLOBS) +# As workaround, $(RIOTBASE)/Makefile.include passes the above-listed variables +# to this Makefile as APPLICATION_*. +BLOBS = $(APPLICATION_BLOBS) +SRC = $(APPLICATION_SRC) +SRCXX = $(APPLICATION_SRCXX) +ASMSRC = $(APPLICATION_ASMSRC) +ASSMSRC = $(APPLICATION_ASSMSRC) include $(RIOTBASE)/Makefile.base diff --git a/tests/build_system/src_folders/Makefile b/tests/build_system/src_folders/Makefile new file mode 100644 index 000000000000..cf2ff2c985f6 --- /dev/null +++ b/tests/build_system/src_folders/Makefile @@ -0,0 +1,14 @@ +include ../Makefile.build_system_common + +## Add fmt to read blob +USEMODULE += fmt + +# Add blobs +BLOBS += blob/blob_subdir/blobtest_subdir.txt + +SRC += main.c +SRC += blob/blob_test.c +# # Alternative method to add files in subfolders using wildcards +# # SRC += $(wildcard *.c blob/*.c folder/*.c folder/**/*.c) + +include $(RIOTBASE)/Makefile.include diff --git a/tests/build_system/src_folders/blob/blob_subdir/blobtest_subdir.txt b/tests/build_system/src_folders/blob/blob_subdir/blobtest_subdir.txt new file mode 100644 index 000000000000..75560a77516f --- /dev/null +++ b/tests/build_system/src_folders/blob/blob_subdir/blobtest_subdir.txt @@ -0,0 +1 @@ +Hello blob_subdir! diff --git a/tests/build_system/src_folders/blob/blob_test.c b/tests/build_system/src_folders/blob/blob_test.c new file mode 100644 index 000000000000..97edc2601700 --- /dev/null +++ b/tests/build_system/src_folders/blob/blob_test.c @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2024 Orange + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include "fmt.h" + +#include "blob/blob/blob_subdir/blobtest_subdir.txt.h" + +void blob_print(void) +{ + print((char *)blobtest_subdir_txt, blobtest_subdir_txt_len); + print("\n", 1); +} diff --git a/tests/build_system/src_folders/main.c b/tests/build_system/src_folders/main.c new file mode 100644 index 000000000000..e3ebb33a35c6 --- /dev/null +++ b/tests/build_system/src_folders/main.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 Orange + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test proving SRC subfolders compatibility with RIOT. + * + * @author Pierre Le Meur + * + * + * @} + */ + +#include + +void blob_print(void); + +int main(void) +{ + + /* blob test */ + blob_print(); + + return 0; +}