I am trying to configure to my build process for ESP-IDF to slim down the build. What I have tried : I've added custom entries to the menuconfig:
menu "Install Sensor Device Drivers"
config LISD3H_CHECK
bool "Include LISD3H Accel Sensor"
default "n"
help
Install required drivers for LISD3H and check if device present
config SHT30_CHECK
bool "Include SHT30 Temp & Hum Sensor"
default "n"
help
Install required drivers for SHT30 and check if device present
endmenu
and tried using these to reduce unnecessary components getting compiled into the project. The config variables are correctly created and accessible within the source code. Now, I tried to control the build within the top level cmake file:
set(component_dirs "${CMAKE_CURRENT_LIST_DIR}/../components/comp1")
message(STATUS ${component_dirs})
message(STATUS ${CONFIG_LISD3H_CHECK})
message(STATUS ${CONFIG_SHT30_CHECK})
if(CONFIG_LISD3H_CHECK)
list(APPEND component_dirs "${CMAKE_CURRENT_LIST_DIR}/../components/comp2" )
message(STATUS ${component_dirs})
else()
message(STATUS "config not equal to whategver...")
endif()
However, the if statement is always false because cmake cannot see the CONFIG_XXXX variables from Kconfig (the messages are blank) Is there a way to access Kconfig varaibles inside cmake? Or a means of using menuconfig to control the build?
Basically, How do you exclude a component from the build? Should I just use C precompile directives inside the source code and ignore what gets compiled, since it will get optimised out of the final binary?
You should be on the correct path according to the ESP IDF build system documentation. Did you add your options to Kconfig in the main component? Note that you need to actually run idf.py menuconfig and choose your configuration before it becomes visible to cmake, after which the root sdkconfig file should have them set.
For example, in the ESP IDF project's main/Kconfig.projbuild file I added
menu "Test Configuration"
config TESTOPTION_YAY
bool "Enable test option"
endmenu
Then in the project root CMakeLists.txt I added:
message(STATUS CONFIG_TESTOPTION_YAY: ${CONFIG_TESTOPTION_YAY})
Now I run idf.py menuconfig and set the test option on or off. Subsequently I build with idf.py build - the build log shows -- CONFIG_TESTOPTION_YAY:y or -- CONFIG_TESTOPTION_YAY: depending on the choice I made.
We are building an application on OS X, the build management being implemented in CMake.
In the CMakeLists.txt of the application, the target is added as:
add_executable(${PROJECT_NAME} MACOSX_BUNDLE
${${PROJECT_NAME}_HEADERS}
${${PROJECT_NAME}_SOURCES}
${${PROJECT_NAME}_RESOURCES}
main.mm
)
With ${PROJECT_NAME}_RESOURCES variable listing the xib and image files. We also set the target properties and configure the installation as follows:
set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION "${VERSION}"
SOVERSION "${SOVERSION}"
RESOURCE "${${PROJECT_NAME}_RESOURCES}"
)
install(TARGETS ${PROJECT_NAME}
BUNDLE DESTINATION ${RUNTIME_OUTPUT_DIRECTORY}
)
Quoting the documentation (emphasis mine):
The PRIVATE_HEADER, PUBLIC_HEADER, and RESOURCE arguments cause
subsequent properties to be applied to installing a FRAMEWORK shared
library target’s associated files on non-Apple platforms. Rules
defined by these arguments are ignored on Apple platforms because the
associated files are installed into the appropriate locations inside
the framework folder.
So we would understand that specifing a RESOURCE argument to install() would be ignored in our situation. Yet CMake issues this message (in red):
INSTALL TARGETS - target Xxx has RESOURCE files but no RESOURCE
DESTINATION.
EDIT: Setting a value for RESOURCE DESTINATION in the install() command disables the message. In this case, the resources are still copied in the right place into the application bundle, but they are also copied into the provided value.
Is there at least a way to turn off this message without introducing copies that are useless on OS X ?
Vísual Studio >=2010 does provide to configure system directories in the VC++ Directories section. Is there any way to tell CMake to fill these settings instead using C/C++/Additional Include Directories?
Since cmake 3.12 you can use variables like CMAKE_VS_SDK_INCLUDE_DIRECTORIES to setup "VC++ Include Directories".
Simply add the following line to your CMakeLists.txt:
include_directories(-your-include-folder-)
Similarly, add the following line if you want to set the library directories:
link_directories(-your-library-folder-)
As Min said, you can set CMAKE_VS_SDK_INCLUDE_DIRECTORIES now. Thank you Min!
I'll leave a working example for the next person that ends up here like I did.
cmake_minimum_required(VERSION 3.18)
# ...
if (MSVC)
# Example for a library that only exports as module without xxx_DIRS variables.
get_target_property(SDL2_image_INCLUDE_DIRS SDL2_image::SDL2_image INTERFACE_INCLUDE_DIRECTORIES)
# Join all include dependencies in a list
set(_DEPS_DIRS ${SDL2_INCLUDE_DIRS} ${SDL2_image_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS})
# Make all slashes turn into backslashes
cmake_path(CONVERT "${_DEPS_DIRS}" TO_NATIVE_PATH_LIST _NATIVE_DIRS NORMALIZE)
# Set the "VC++ Directories > Include Directories" setting
set(CMAKE_VS_SDK_INCLUDE_DIRECTORIES "$(VC_IncludePath);$(WindowsSDK_IncludePath)" ${_NATIVE_DIRS})
unset(_DEPS_DIRS)
unset(_NATIVE_DIRS)
endif()
Important: To troubleshoot first delete the build folder and start from scratch.
I believe previously (before v3.18?) cmake added the list to the "Additional Include Directories" setting, but I think they removed it because those are added with /I compiler option, and now it seems included dependencies are added with /external:I if you look at Command Line settings in the project, and they may conflict? Not confirmed, just a thought.
Don't use target_include_directories or include_directories just for this. With modules you shouldn't need to, only use it if you already had to.
Also even if possible shouldn't include all headers as dependencies to add_executable, that's inconvenient.
We are currently building an SDK for a customer using CocoaPods.
The main problem we have is that our boss would like the SDK to be a black box. He wants us to precompile the code in order to protect our source.
Is there anything we can do within the Podspec in order to protect our code?
You can do exactly that by creating a Static Framework and including it in the spec.vendored_frameworks property on your podspec.
http://guides.cocoapods.org/syntax/podspec.html#vendored_frameworks
Follow the tutorial below for how to create your own static framework.
https://github.com/jverkoey/iOS-Framework#walkthrough
How to Create a Static Framework for iOS
There are a few constraints that we want to satisfy when building a .framework:
Fast iterative builds when developing the framework. We may have a simple application that has the
.framework as a dependency and we want to quickly iterate on development of the .framework.
Infrequent distribution builds of the .framework.
Resource distribution should be intuitive and not bloat the application.
Setup for third-party developers using the .framework should be easy.
I believe that the solution I will outline below satisfies each of these constraints. I will outline
how to build a .framework project from scratch so that you can apply these steps to an existing
project if you so desire. I will also include project templates for easily creating a
.framework.
Overview
View a sample project that shows the result of following these steps in the sample/Serenity
directory.
Within the project we are going to have three targets: a static library, a bundle, and an aggregate.
The static library target will build the source into a static library (.a) and specify which headers
will be "public", meaning they will be accessible from the .framework when we distribute it.
The bundle target will contain all of our resources and will be loadable from the framework.
The aggregate target will build the static library for i386/armv6/armv7/armv7s, generate the fat framework
binary, and also build the bundle. You will run this target when you plan to distribute the
.framework.
When you are working on the framework you will likely have an internal application that links to the
framework. This application will link to the static library target as you normally would and copy
the .bundle in the copy resources phase. This has the benefit of only building the framework code
for the platform you're actively working on, significantly improving your build times. We'll do a
little bit of work in the framework project to ensure that you can use your framework in your app
the same way a third party developer would (i.e. importing should work
as expected). Jump to the dependent project walkthrough.
Create the Static Library Target
Step 1: Create a New "Cocoa Touch Static Library" Project
The product name will be the name of your framework. For example, Serenity will generate
Serenity.framework once we've set up the project.
Step 2: Create the Primary Framework Header
Developers expect to be able to import your framework by importing the <Serenity/Serenity.h>
header. Ensure that your project has such a header (if you created a new static library then there
should already be a Serenity.h and Serenity.m file; you can delete the .m).
Within this header you are going to import all of the public headers for your framework. For
example, let's assume that we have some Widget with a .h and .m. Our Serenity.h file would look
like this:
#import <Foundation/Foundation.h>
#import <Serenity/Widget.h>
Once you've created your framework header file, you need to make it a "public" header. Public
headers are headers that will be copied to the .framework and can be imported by those using your
framework. This differs from "project" headers which will not be distributed with the framework.
This distinction is what allows you to have a concept of public and private APIs.
To change a file's [target membership visibility in XCode 4.4+]
(Can't change target membership visibility in Xcode 4.5),
you'll need to select the Static Library target you created (Serenity), open the Build Phases tab:
Xcode 4.X:
Click on Add Build Phase > Add Copy Headers.
Xcode 5:
Add Build Phases from the menu. Click on Editor > Add Build Phase -> Add Copy Headers Build Phase. Note: If the menu options are grayed out, you'll need to click on the whitespace below the Build Phases to regain focus and retry.
You'll see 3 sections for Public, Private, and Project headers. To modify the scope of any header, drag and drop the header files between the sections. Alternatively you can open the Project Navigator and select the header. Next expand the Utilities pane for the File Inspector.
(Cmd+Option+0).
Look at the "Target Membership" group and ensure that the checkbox next to the .h file is checked.
Change the scope of the header from "Project" to "Public". You might have to uncheck and check the box to get the dropdown list. This will ensure that the header gets
copied to the correct location in the copy headers phase.
Step 3: Update the Public Headers Location
By default the static library project will copy private and public headers to the same folder:
/usr/local/include. To avoid mistakenly copying private headers to our framework we want to ensure
that our public headers are copied to a separate directory, e.g. $(PROJECT_NAME)Headers. To change this setting,
select the project in the Project Navigator and then click the "Build Settings" tab. Search for "public
headers" and then set the "Public Headers Folder Path" to "$(PROJECT_NAME)Headers" for all configurations.
If you are working with multiple Frameworks make sure that this folder is unique.
Ongoing Step: Adding New Sources to the Framework
Whenever you add new source to the framework you must decide whether to expose the .h publicly or
not. To modify a header's scope you will follow the same process as Step 2. By default a header's
scope will be "Project", meaning it will not be copied to the framework's public headers.
Step 4: Disable Code Stripping
We do not want to strip any code from the library; we leave this up to the application that is
linking to the framework. To disable code stripping we must modify the following configuration
settings:
"Dead Code Stripping" => No (for all settings)
"Strip Debug Symbols During Copy" => No (for all settings)
"Strip Style" => Non-Global Symbols (for all settings)
Step 5: Prepare the Framework for use as a Dependent Target
In order to use the static library as though it were a framework we're going to generate the basic
skeleton of the framework in the static library target. To do this we'll include a simple post-build
script. Add a post-build script by selecting your project in the Project Navigator, selecting the target, and then the
"Build Phases" tab.
Xcode 4.X: Click Add Build Phase > Add Run Script
Xcode 5: Select Editor menu > Add Build Phase > Add Run Script Build Phase
Paste the following script in the source portion of the run script build phase. You can rename the phase by clicking
the title of the phase (I've named it "Prepare Framework", for example).
prepare_framework.sh
set -e
mkdir -p "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/A/Headers"
# Link the "Current" version to "A"
/bin/ln -sfh A "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/Current"
/bin/ln -sfh Versions/Current/Headers "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Headers"
/bin/ln -sfh "Versions/Current/${PRODUCT_NAME}" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"
# The -a ensures that the headers maintain the source modification date so that we don't constantly
# cause propagating rebuilds of files that import these headers.
/bin/cp -a "${TARGET_BUILD_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}/" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/A/Headers"
This will generate the following folder structure:
-- Note: "->" denotes a symbolic link --
Serenity.framework/
Headers/ -> Versions/Current/Headers
Serenity -> Versions/Current/Serenity
Versions/
A/
Headers/
Serenity.h
Widget.h
Current -> A
Try building your project now and look at the build products directory (usually
~/Library/Developer/Xcode/DerivedData/<ProjectName>-<gibberish>/Build/Products/...). You should
see a libSerenity.a static library, a Headers folder, and a Serenity.framework folder that
contains the basic skeleton of your framework.
Create the Framework Distribution Target
When actively developing the framework we only care to build the platform that we're testing on. For
example, if we're testing on the iPhone simulator then we only need to build the i386 platform.
This changes when we want to distribute the framework to third party developers. The third-party
developers don't have the option of rebuilding the framework for each platform, so we must provide
what is called a "fat binary" version of the static library that is comprised of the possible
platforms. These platforms include: i386, armv6, armv7, and armv7s.
To generate this fat binary we're going to build the static library target for each platform.
Step 1: Create an Aggregate Target
Click File > New Target > iOS > Other and create a new Aggregate target. Title it something like "Framework".
Step 2: Add the Static Library as a Dependent Target
Add the static library target to the "Target Dependencies".
Step 3: Build the Other Platform
To build the other platform we're going to use a "Run Script" phase to execute some basic commands.
Add a new "Run Script" build phase to your aggregate target and paste the following code into it.
build_framework.sh
set -e
set +u
# Avoid recursively calling this script.
if [[ $SF_MASTER_SCRIPT_RUNNING ]]
then
exit 0
fi
set -u
export SF_MASTER_SCRIPT_RUNNING=1
SF_TARGET_NAME=${PROJECT_NAME}
SF_EXECUTABLE_PATH="lib${SF_TARGET_NAME}.a"
SF_WRAPPER_NAME="${SF_TARGET_NAME}.framework"
# The following conditionals come from
# https://github.com/kstenerud/iOS-Universal-Framework
if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]
then
SF_SDK_PLATFORM=${BASH_REMATCH[1]}
else
echo "Could not find platform name from SDK_NAME: $SDK_NAME"
exit 1
fi
if [[ "$SDK_NAME" =~ ([0-9]+.*$) ]]
then
SF_SDK_VERSION=${BASH_REMATCH[1]}
else
echo "Could not find sdk version from SDK_NAME: $SDK_NAME"
exit 1
fi
if [[ "$SF_SDK_PLATFORM" = "iphoneos" ]]
then
SF_OTHER_PLATFORM=iphonesimulator
else
SF_OTHER_PLATFORM=iphoneos
fi
if [[ "$BUILT_PRODUCTS_DIR" =~ (.*)$SF_SDK_PLATFORM$ ]]
then
SF_OTHER_BUILT_PRODUCTS_DIR="${BASH_REMATCH[1]}${SF_OTHER_PLATFORM}"
else
echo "Could not find platform name from build products directory: $BUILT_PRODUCTS_DIR"
exit 1
fi
# Build the other platform.
xcrun xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk ${SF_OTHER_PLATFORM}${SF_SDK_VERSION} BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" SYMROOT="${SYMROOT}" $ACTION
# Smash the two static libraries into one fat binary and store it in the .framework
xcrun lipo -create "${BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}" "${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}" -output "${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}"
# Copy the binary to the other architecture folder to have a complete framework in both.
cp -a "${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}" "${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}"
Important Note
The above script assumes that your library name matches your project name in the following line:
SF_TARGET_NAME=${PROJECT_NAME}
If this is not the case (e.g. your xcode project is named SerenityFramework and the target name is
Serenity) then you need to explicitly set the target name on that line. For example:
SF_TARGET_NAME=Serenity
Step 4: Build and Verify
You now have everything set up to build a distributable .framework to third-party developers. Try
building the aggregate target. Once it's done, expand the Products folder in Xcode, right click the
static library and click "Show in Finder". If this doesn't open Finder to where the static library
exists then try opening
~/Library/Developer/Xcode/DerivedData/<project name>/Build/Products/Debug-iphonesimulator/.
Within this folder you will see your .framework folder.
You can now drag the .framework elsewhere, zip it up, upload it, and distribute it to your
third-party developers.
I succeeded using that Podspec as an example :
Pod::Spec.new do |s|
s.name = "EstimoteSDK"
s.version = "1.3.0"
s.summary = "iOS library for Estimote iBeacon devices"
s.homepage = "http://estimote.com"
s.author = { "Estimote, Inc" => "contact#estimote.com" }
s.platform = :ios
s.source = { :git => "https://github.com/Estimote/iOS-SDK.git", :tag => " {s.version}" }
s.source_files = 'EstimoteSDK/Headers/*.h'
s.preserve_paths = 'EstimoteSDK/libEstimoteSDK.a'
s.vendored_libraries = 'EstimoteSDK/libEstimoteSDK.a'
s.ios.deployment_target = '7.0'
s.frameworks = 'UIKit', 'Foundation', 'SystemConfiguration', 'MobileCoreServices', 'CoreLocation'
s.requires_arc = true
s.xcconfig = { 'LIBRARY_SEARCH_PATHS' => '"$(PODS_ROOT)/EstimoteSDK"',
'HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/Headers/EstimoteSDK"' }
s.license = {
:type => 'Copyright',
:text => <<-LICENSE
Copyright 2013 Estimote, Inc. All rights reserved.
LICENSE
}
end
Static Libraries are not supported in Swift, so for anyone coming here looking for the solution for a Swift SDK, here is a nice article explaining how it should be done.
Update
Swift 4 now natively supports static swift libraries.
Side note:
For Anyone still interested in creating a swift dynamic library, this is still a nice, very helpful article article
I know how to link different libraries based on whether build configuration is Debug or Release. I use:
foreach(dep ${DEPENDENCIES})
target_link_libraries (${PROJECT_NAME}
debug ${dep}_d
optimized ${dep}
)
endforeach(dep)
CMake by default create 4 build configurations in VS2010 (Debug, Release, RelWithDebugInfo, MinSizeRelease). But how to define taget link libraries for RelWithDebugInfo configuration?
Bakcground:
I use only Debug, Release and RelWithDebugInfo. My debug libraries have suffix _d and others have no suffix. So output files from Release and RelWithDebugInfo are the same. Sometimes when I build RelWithDebugInfo and then Release some output files are not overwritten and thus bad ones are loaded and program crashes. I want to solve this problem by adding some other suffix to RelWithDebugInfo configuration.
I have found the solution. It is impossible to do via target_link_librearies but it can be done by setting linker flags:
set(DEBUG_DEP)
set(RWD_DEP)
set(RELEASE_DEP)
foreach(dep ${DEPENDENCIES})
set(RWD_DEP ${RWD_DEP} ${dep}_rwd)
set(DEBUG_DEP ${DEBUG_DEP} ${dep}_d)
set(RELEASE_DEP ${RELEASE_DEP} ${dep})
endforeach(dep)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG ${CMAKE_SHARED_LINKER_FLAGS_DEBUG} " /LIBPATH:" ${DEBUG_DEP})
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE ${CMAKE_SHARED_LINKER_FLAGS_RELEASE} " /LIBPATH:" ${RELEASE_DEP})
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}" /LIBPATH:" ${RWD_DEP})