Skip to content
This repository was archived by the owner on Apr 17, 2023. It is now read-only.

Commit ed09635

Browse files
authored
Merge pull request #36 from arduino-cmake/feature/header-only-lib
Added support for header-only libraries
2 parents ada111e + 36df836 commit ed09635

File tree

13 files changed

+175
-49
lines changed

13 files changed

+175
-49
lines changed

cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,20 @@
66
#=============================================================================#
77
function(_set_library_flags _library_target _board_id)
88

9+
set(scope_options "PRIVATE" "PUBLIC" "INTERFACE")
10+
cmake_parse_arguments(parsed_args "${scope_options}" "" "" ${ARGN})
11+
12+
if (parsed_args_PRIVATE)
13+
set(scope PRIVATE)
14+
elseif (parsed_args_INTERFACE)
15+
set(scope INTERFACE)
16+
else ()
17+
set(scope PUBLIC)
18+
endif ()
19+
920
# Set C++ compiler flags
1021
get_cmake_compliant_language_name(cpp flags_language)
11-
set_compiler_target_flags(${_library_target} "${_board_id}" PUBLIC LANGUAGE ${flags_language})
22+
set_compiler_target_flags(${_library_target} "${_board_id}" ${scope} LANGUAGE ${flags_language})
1223

1324
# Set linker flags
1425
set_linker_flags(${_library_target} "${_board_id}")
@@ -22,37 +33,45 @@ endfunction()
2233
# _target_name - Name of the library target to be created. Usually library's real name.
2334
# _board_id - Board ID associated with the linked Core Lib.
2435
# _sources - Source and header files to create library target from.
25-
# [ARCH] - Optional library architecture (Such as 'avr', 'nrf52', etc.)
36+
# [ARCH] - Optional library architecture (Such as 'avr', 'nrf52', etc.).
37+
# [INTERFACE] - Whether the library should be created as an interface library (header-only).
2638
#=============================================================================#
2739
function(_add_arduino_cmake_library _target_name _board_id _sources)
2840

29-
cmake_parse_arguments(library "" "ARCH" "" ${ARGN})
41+
cmake_parse_arguments(parsed_args "INTERFACE" "ARCH" "" ${ARGN})
3042

31-
if (library_ARCH) # Treat architecture-specific libraries differently
43+
if (parsed_args_ARCH) # Treat architecture-specific libraries differently
3244
# Filter any sources that aren't supported by the platform's architecture
3345
list(LENGTH library_ARCH num_of_libs_archs)
3446
if (${num_of_libs_archs} GREATER 1)
3547
# Exclude all unsupported architectures, request filter in regex mode
36-
_get_unsupported_architectures("${library_ARCH}" arch_filter REGEX)
48+
_get_unsupported_architectures("${parsed_args_ARCH}" arch_filter REGEX)
3749
set(filter_type EXCLUDE)
3850
else ()
39-
set(arch_filter "src\\/[^/]+\\.|${library_ARCH}")
51+
set(arch_filter "src\\/[^/]+\\.|${parsed_args_ARCH}")
4052
set(filter_type INCLUDE)
4153
endif ()
4254
list(FILTER _sources ${filter_type} REGEX ${arch_filter})
4355
endif ()
4456

45-
add_library(${_target_name} STATIC "${_sources}")
57+
if (parsed_args_INTERFACE)
58+
add_library(${_target_name} INTERFACE)
59+
set(scope INTERFACE)
60+
else ()
61+
add_library(${_target_name} STATIC "${_sources}")
62+
set(scope PUBLIC)
63+
endif ()
64+
4665
# Treat headers' parent directories as include directories of the target
4766
get_headers_parent_directories("${_sources}" include_dirs)
48-
target_include_directories(${_target_name} PUBLIC ${include_dirs})
67+
target_include_directories(${_target_name} ${scope} ${include_dirs})
4968

50-
_set_library_flags(${_target_name} ${_board_id})
69+
_set_library_flags(${_target_name} ${_board_id} ${scope})
5170

52-
if (library_ARCH)
53-
string(TOUPPER ${library_ARCH} upper_arch)
71+
if (parsed_args_ARCH)
72+
string(TOUPPER ${parsed_args_ARCH} upper_arch)
5473
set(arch_definition "ARDUINO_ARCH_${upper_arch}")
55-
target_compile_definitions(${_target_name} PUBLIC ${arch_definition})
74+
target_compile_definitions(${_target_name} ${scope} ${arch_definition})
5675
endif ()
5776

5877
endfunction()
@@ -76,6 +95,15 @@ function(_link_arduino_cmake_library _target_name _library_name)
7695
set(scope_options "PRIVATE" "PUBLIC" "INTERFACE")
7796
cmake_parse_arguments(link_library "${scope_options}" "BOARD_CORE_TARGET" "" ${ARGN})
7897

98+
# Now, link library to executable
99+
if (link_library_PUBLIC)
100+
set(scope PUBLIC)
101+
elseif (link_library_INTERFACE)
102+
set(scope INTERFACE)
103+
else ()
104+
set(scope PRIVATE)
105+
endif ()
106+
79107
# First, include core lib's directories in library as well
80108
if (link_library_BOARD_CORE_TARGET)
81109
set(core_target ${link_library_BOARD_CORE_TARGET})
@@ -84,17 +112,9 @@ function(_link_arduino_cmake_library _target_name _library_name)
84112
endif ()
85113

86114
get_target_property(core_lib_includes ${core_target} INCLUDE_DIRECTORIES)
87-
target_include_directories(${_library_name} PUBLIC "${core_lib_includes}")
88-
target_link_libraries(${_library_name} PUBLIC ${core_target})
115+
target_include_directories(${_library_name} ${scope} "${core_lib_includes}")
116+
target_link_libraries(${_library_name} ${scope} ${core_target})
89117

90-
# Now, link library to executable
91-
if (link_library_PUBLIC)
92-
set(scope PUBLIC)
93-
elseif (link_library_INTERFACE)
94-
set(scope INTERFACE)
95-
else ()
96-
set(scope PRIVATE)
97-
endif ()
98-
target_link_libraries(${_target_name} ${scope} ${_library_name})
118+
target_link_libraries(${_target_name} PRIVATE ${_library_name})
99119

100120
endfunction()

cmake/Platform/Targets/ArduinoLibraryTarget.cmake

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ function(add_arduino_library _target_name _board_id _sources)
8686

8787
endfunction()
8888

89+
function(add_arduino_header_only_library _target_name _board_id)
90+
91+
cmake_parse_arguments(parsed_args "ARCH" "" "HEADERS" ${ARGN})
92+
93+
_add_arduino_cmake_library(${_target_name} ${_board_id} "${parsed_args_HEADERS}"
94+
INTERFACE ${parsed_args_ARCH})
95+
96+
endfunction()
97+
8998
#=============================================================================#
9099
# Finds an Arduino library with the given library name and creates a library target from it
91100
# with the given target name.
@@ -95,12 +104,14 @@ endfunction()
95104
# _library_name - Name of the Arduino library to find.
96105
# _board_id - Board ID associated with the linked Core Lib.
97106
# [3RD_PARTY] - Whether library should be treated as a 3rd Party library.
107+
# [HEADER_ONLY] - Whether library is a header-only library, i.e has no source files
98108
#=============================================================================#
99109
function(find_arduino_library _target_name _library_name _board_id)
100110

101-
cmake_parse_arguments(find_lib "3RD_PARTY" "" "" ${ARGN})
111+
set(argument_options "3RD_PARTY" "HEADER_ONLY")
112+
cmake_parse_arguments(parsed_args "${argument_options}" "" "" ${ARGN})
102113

103-
if (NOT find_lib_3RD_PARTY)
114+
if (NOT parsed_args_3RD_PARTY)
104115
convert_string_to_pascal_case(${_library_name} _library_name)
105116
endif ()
106117

@@ -130,13 +141,23 @@ function(find_arduino_library _target_name _library_name _board_id)
130141
set(error_message "Couldn't find any header files for the ${_library_name} library")
131142
message(SEND_ERROR "${error_message}")
132143
else ()
133-
find_library_source_files("${library_path}" library_sources)
134-
if (NOT library_sources)
135-
set(error_message "Couldn't find any source files for the ${_library_name} library")
136-
message(SEND_ERROR "${error_message}")
144+
if (parsed_args_HEADER_ONLY)
145+
add_arduino_header_only_library(${_target_name} ${_board_id}
146+
ARCH ${lib_arch}
147+
HEADERS ${library_headers})
137148
else ()
138-
set(sources ${library_headers} ${library_sources})
139-
add_arduino_library(${_target_name} ${_board_id} "${sources}" ARCH ${lib_arch})
149+
find_library_source_files("${library_path}" library_sources)
150+
if (NOT library_sources)
151+
string(CONCAT error_message
152+
"Couldn't find any source files for the ${_library_name} library - "
153+
"Is it a header-only library?\n"
154+
"If so, please pass the HEADER_ONLY option as an argument to the function")
155+
message(SEND_ERROR "${error_message}")
156+
else ()
157+
set(sources ${library_headers} ${library_sources})
158+
add_arduino_library(${_target_name} ${_board_id} "${sources}"
159+
ARCH ${lib_arch})
160+
endif ()
140161
endif ()
141162
endif ()
142163
endif ()
@@ -151,9 +172,12 @@ endfunction()
151172
# _target_name - Name of the "executable" target.
152173
# _library_target_name - Name of the library target.
153174
# _board_id - Board ID associated with the linked Core Lib.
175+
# [HEADER_ONLY] - Whether library is a header-only library, i.e has no source files
154176
#=============================================================================#
155177
function(link_arduino_library _target_name _library_target_name _board_id)
156178

179+
cmake_parse_arguments(parsed_args "HEADER_ONLY" "" "" ${ARGN})
180+
157181
get_core_lib_target_name(${_board_id} core_lib_target)
158182

159183
if (NOT TARGET ${_target_name})
@@ -164,8 +188,14 @@ function(link_arduino_library _target_name _library_target_name _board_id)
164188
message(FATAL_ERROR "Core Library target doesn't exist. This is bad and should be reported")
165189
endif ()
166190

167-
_link_arduino_cmake_library(${_target_name} ${_library_target_name}
168-
PUBLIC
169-
BOARD_CORE_TARGET ${core_lib_target})
191+
if (parsed_args_HEADER_ONLY)
192+
_link_arduino_cmake_library(${_target_name} ${_library_target_name}
193+
INTERFACE
194+
BOARD_CORE_TARGET ${core_lib_target})
195+
else ()
196+
_link_arduino_cmake_library(${_target_name} ${_library_target_name}
197+
PUBLIC
198+
BOARD_CORE_TARGET ${core_lib_target})
199+
endif ()
170200

171201
endfunction()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <Arduino.h>
2+
#include "NeoPixelTest.hpp"
3+
#include "GFXTest.h"
4+
5+
void setup()
6+
{
7+
testNeoPixel();
8+
testGFX();
9+
}
10+
11+
void loop()
12+
{
13+
14+
}

examples/3rd-party-library/CMakeLists.txt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,23 @@ cmake_minimum_required(VERSION 3.8.2)
33
project(3rd_Party_Arduino_Library)
44
get_board_id(board_id nano atmega328)
55

6-
add_arduino_executable(3rd_Party_Arduino_Library ${board_id} NeoPixelTest.cpp GFXTest.cpp)
6+
# First, declare and create our executable - It'll use 4 sources
7+
add_arduino_executable(3rd_Party_Arduino_Library ${board_id} 3rd_party.cpp
8+
NeoPixelTest.cpp GFXTest.cpp)
79
target_include_directories(3rd_Party_Arduino_Library PRIVATE include)
810

11+
# Add the "NeoPixel" library manually using the library addition API
912
add_arduino_library(Adafruit_NeoPixel ${board_id} libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.cpp)
1013
target_include_directories(Adafruit_NeoPixel PUBLIC libraries/Adafruit_NeoPixel)
1114

12-
# 'Trick' the framework to use current directory as Sketchbook, allowing us to use the 'find' API
15+
# Find the "GFX" library by 'tricking' the framework to use current directory as the Sketchbook path,
16+
# allowing us to use the 'find' API
1317
set(ARDUINO_CMAKE_SKETCHBOOK_PATH "${CMAKE_CURRENT_LIST_DIR}")
1418
find_arduino_library(Adafruit_GFX Adafruit-GFX-Library ${board_id} 3RD_PARTY)
15-
target_source_directories(Adafruit_GFX
16-
DIRS libraries/Adafruit-GFX-Library/Fonts)
19+
# We can also explicitly add additional directories to the target,
20+
# as only root dir and 'src' sub-dir are added by default
21+
target_source_directories(Adafruit_GFX DIRS libraries/Adafruit-GFX-Library/Fonts)
1722

23+
# Link all libraries to our previously created target
1824
link_arduino_library(3rd_Party_Arduino_Library Adafruit_NeoPixel ${board_id})
1925
link_arduino_library(3rd_Party_Arduino_Library Adafruit_GFX ${board_id})

examples/3rd-party-library/GFXTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
static Adafruit_GFX_Button gfxButton;
44

5-
void doSomething()
5+
void testGFX()
66
{
77
gfxButton.isPressed();
88
}
Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
#include <Adafruit_NeoPixel.h>
2-
#include "GFXTest.h"
1+
2+
#include <NeoPixelTest.hpp>
3+
4+
#include "include/NeoPixelTest.hpp"
35

46
Adafruit_NeoPixel neoPixel;
57

6-
void setup()
8+
void testNeoPixel()
79
{
810
neoPixel.clear();
9-
doSomething();
10-
}
11-
12-
void loop()
13-
{
14-
1511
}

examples/3rd-party-library/include/GFXTest.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33

44
#include <Adafruit_GFX.h>
55

6-
void doSomething();
6+
void testGFX();
77

88
#endif //EXAMPLES_GFXTEST_HPP
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef EXAMPLES_NEOPIXELTEST_HPP
2+
#define EXAMPLES_NEOPIXELTEST_HPP
3+
4+
#include <Adafruit_NeoPixel.h>
5+
6+
void testNeoPixel();
7+
8+
#endif //EXAMPLES_NEOPIXELTEST_HPP

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ project(Examples LANGUAGES C CXX ASM)
55
add_subdirectory(hello-world)
66
add_subdirectory(arduino-library)
77
add_subdirectory(3rd-party-library)
8+
add_subdirectory(header-only-library)
89
add_subdirectory(blink-example)
910
add_subdirectory(servo-knob-example)
1011
add_subdirectory(sketch)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
cmake_minimum_required(VERSION 3.8.2)
2+
3+
project(Header_Only_Lib)
4+
get_board_id(board_id nano atmega328)
5+
6+
add_arduino_executable(Header_Only_Lib ${board_id} headerOnlyTest.cpp)
7+
8+
# Find the library by 'tricking' the framework to use current directory as the Sketchbook path,
9+
# allowing us to use the 'find' API
10+
set(ARDUINO_CMAKE_SKETCHBOOK_PATH "${CMAKE_CURRENT_LIST_DIR}")
11+
find_arduino_library(headerTest test-lib ${board_id} 3RD_PARTY HEADER_ONLY)
12+
13+
link_arduino_library(Header_Only_Lib headerTest ${board_id} HEADER_ONLY)

0 commit comments

Comments
 (0)