I have a header only library I want to write a CMake file for, to be used in other projects. To do this I need to declare the library an interface target. The header files are then added by CMakeList.txt files in subfolders.
Now I want to get the list of source files to run clang-format on them. But get_target_property does not work on interface targets. Is there any way to extract the sources (or headers) from an interface target?
This is the code I have at the moment (not working)
make_minimum_required(VERSION 3.12)
project(foo
LANGUAGES CXX
VERSION 1.0.0)
add_library(bar INTERFACE)
# add header files in sub folders with
target_sources(bar INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/header.h"
)
# this gives the error
get_target_property(MY_SOURCES bar SOURCES)
The error message
INTERFACE_LIBRARY targets may only have whitelisted properties. The
property "SOURCES" is not allowed.
I tried to link the interface target into a real target and get the sources that way, but I only got the sources added directly to the second target, not the sources from the interface target
Because your target is declared as INTERFACE, the target_sources() command populates the INTERFACE_SOURCES property of the target, not SOURCES. From the docs:
The INTERFACE, PUBLIC and PRIVATE keywords are required to specify the scope of the following arguments. PRIVATE and PUBLIC items will populate the SOURCES property of <target>. PUBLIC and INTERFACE items will populate the INTERFACE_SOURCES property of <target>.
Try grabbing the INTERFACE_SOURCES property instead:
add_library(bar INTERFACE)
# add header files in sub folders with
target_sources(bar INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/header.h"
)
# this gives the error
get_target_property(MY_SOURCES bar INTERFACE_SOURCES)
Related
I understand that target_include_directories() can be used in conjunction with PUBLIC, INTERFACE, or PRIVATE to specify the scope or privacy of the directories. But a lot of antiquated code still uses include_directories(), and I'm not sure how these directories are treated by the targets that use them.
Is using include_directories() essentially the same as using target_include_directories(MyTarget PRIVATE ...)? I have seen this similar question, but is there no default scoping behavior with include_directories() either?
Is using include_directories() essentially the same as using target_include_directories(MyTarget PRIVATE ...)?
Yes, for every target, affected by include_directories (that is, in the current CMakeLists.txt and below), it has the same effect as PRIVATE keyword in target-specific version of the command.
Documentation for include_directories says, that the command assigns INCLUDE_DIRECTORIES property of the targets and directories:
The include directories are added to the INCLUDE_DIRECTORIES directory property for the current CMakeLists file. They are also added to the INCLUDE_DIRECTORIES target property for each target in the current CMakeLists file.
and the directory property affects on further target's ones:
This property is used to populate the INCLUDE_DIRECTORIES target property.
Exactly INCLUDE_DIRECTORIES target property is affected by the PRIVATE keyword of target_include_directories command:
PRIVATE and PUBLIC items will populate the INCLUDE_DIRECTORIES property of <target>. PUBLIC and INTERFACE items will populate the INTERFACE_INCLUDE_DIRECTORIES property of <target>.
I have an existing Obj-C project that houses many targets that all share the same AppDelegate. I want to bridge a swift class that is used by select targets. I can do this easily when I have one target.
When I add a swift file to the project, I select the desired targets and the necessary bridging-header.h files are generated, but when I try to import those -swift.h files, they are can't be found.
Are there steps I'm missing when it comes to projects that have multiple build targets?
EDIT - More Details
I wanted to add a little bit more detail on how my project is set up.
I have a Framework, we'll call it AppFactory, coded in Obj-C. I have multiple build targets that generate different versions of the Core app depending on information in that target's plist. I want a swift file to be utilized by these apps. In my build settings the Defines Module is marked to Yes, I have create this swift class:
#objec class SwiftClass: NSObject { }
And in doing that, Xcode generated the proper Briding-Header.h files.
According to Apple Guides when you have multiple build targets your import header should include the ProductName/ProductModuleName-Swift.h, which should be auto generated by Xcode.
When I look in to my derived data folder, the ProductModuleName-Swift.h does exist, with in each targets build folder, under the AppFactoryCore folder.
I found a solution to my problem.
Swift File Targets:
Instead of having SwiftClass.swift target both the framework and the selected targets (AppA, AppB & AppC), I backpedaled and solely targeted the framework, AppFactoryCore.
Build Settings (Packaging > Defines Module):
I reverted each app target's Defines Module property from YES to NO, and set this property to YES for the framework target.
Swift Class Declaration:
The guide states:
Because the generated header for a framework target is part of the frameworkâs public interface, only declarations marked with the public or open modifier appear in the generated header for a framework target.
So I added access control modifiers to my class and class functions
#objc open class SwiftClass: NSObject {
//Code
}
Import Header:
Since SwiftClass.swift is only targeting the framework target, and it is in fact a framework that is being used, the header import SwiftClass.swift into the universal AppDelegate was
#import <AppFactoryCore/AppFactoryCore-Swift.h>
Which finally became visible once all previously stated modifications were done.
Now that the file is global to all targets I added a custom attribute to identify if the target running was is one that should utilize SwiftClass.swift.
Hope this helps anyone else trying to accomplish a relatively similar task!
In foo/CMakeLists.txt, based on this and this, I have the following
SET (EXTERNAL_LIB_ROOT "../../external_libs/")
ADD_LIBRARY (avcodec-debug STATIC IMPORTED)
SET_PROPERTY (
TARGET avcodec-debug PROPERTY IMPORTED_LOCATION
${EXTERNAL_LIB_ROOT}/libavcodec-0.8.10.a)
In bar/CMakeLists.txt I have this:
# old way uses system libraries
#TARGET_LINK_LIBRARIES (bar avformat avcodec avutil)
# new way uses local debug builds
TARGET_LINK_LIBRARIES (bar avformat avcodec-debug avutil)
When I run make I get
/usr/bin/ld: cannot find -lavcodec-debug
If I revert to the old way, build, touch foo/CMakeLists.txt and rebuild, CMake's configuration output indicates that avcodec-debug is being found by the build system.
So why can't I add it as a dependency?
As it mentioned above by Angew the visibility for imported library differs, though you can extend it using GLOBAL modifier. It might be enough for you to modify add_library call next way:
ADD_LIBRARY(avcodec-debug STATIC IMPORTED GLOBAL)
Imported targets do not follow the same visibility rules as non-imported targets. While non-imported targets are global (visible and accessible from anywhere after they're defined), imported targets are only visible in the CMakeLists.txt where they are defined and below (in directories added by add_subdirectory() in this defining CMakeList).
Since foo is a sibling of bar in your case, the target name avcodec-debug is not visible inside bar/CMakeLists.txt, so it's treated as a normal library name.
It's generally preferred to define imported targets in files you include rather than in their own projects. So change (or extract the relevant parts of) foo/CMakeLists.txt into foo/avcodec.cmake and then in the top-level CMakeList, replace
add_subdirectory(foo)
with
include(foo/avcodec.cmake)
Is there a way to force doxygen to ignore all class extensions in an Objective-C project without using preprocessor directives?
I use class extensions extensively in my Class.m files for my private attributes. When I use doxygen to generate documentation for my application, the generated class list includes all of my "secret" class extensions. I would like to hide these extensions from the generated documentation.
I can get doxygen to ignore a particular class extension by using the doxygen preprocessor and surrounding the extension in the Class.m with a #ifndef/#endif. I really don't like adding this to all of my Class.m files that have the internal extensions, because I feel that I am adding more clutter to my files.
If you want exclude all the class continuation add this row to the Doxy file
EXCLUDE_SYMBOLS = *()
The class extensions can be excluded without changing the source code. This can be done by listing the class extensions in the exclude_symbols setting. See the sample screenshot:
I'm building a static library, MyLibrary, for iOS in Objective-C that bundles together a dozen useful classes, each with its own .h file. I'd like to distribute MyLibrary as a single compiled binary, libMyLibrary.a, and a single .h header file, MyLibraryAPI.h. MyLibraryAPI.h has a dozen #import statements, one for each of MyLibrary's dozen public classes. Developers who want to include MyLibrary in their host projects should only have to include the libMyLibrary.a binary and the MyLibraryAPI.h header. This is the goal.
So I have set the Role of each public class in the MyLibrary Xcode project to Public and built libMyLibrary.a successfully using Xcode command line build utils and lipo. Then, I manually included all of the dozen MyLibrary header files along with libMyLibrary.a in a host project, and the host project can use the public MyLibrary classes with no problem. Awesome!
The problem is if I remove those dozen header files and use MyLibraryAPI.h instead (as is my goal), the host project's classes can no longer find the MyLibrary header files referenced in MyLibraryAPI.h. Instead, at compile time, I get errors like: MyAwesomeThingDelegate.h: No such file or directory... for each MyLibrary class that I try to #import in MyLibraryAPI.h. I have a folder in my host project root directory called lib and in host project build settings have set the recursive header search path to lib/** and in Library Search Path, set a recursive path to lib/**.
I'd love to hear suggestions from the community on how to correctly set the host project's search paths so that I only need to include libMyLibrary.a and MyLibraryAPI.h to use the MyLibrary classes. Or if I'm doing something wrong, I'd love to hear another suggestion to achieve my goal of distributing a single binary and a single API header file.
I've had the same challenge and I've come with the following solution:
First, I tried to hide as many implementation details as possible. For that reason, I usually built pairs of classes: one class is the public interface and the other one the private implementation. The public class has only one member instance: the pointer to the implementation. The private class has just a forward declaration.
#class MyViewImpl;
#interface MyView : UIView
{
#private
MyViewImpl* _internal;
}
#property (nonatomic, assign) CGRect highlightArea;
- (void) startAnimation;
#end
Second, I put all the public declarations into a single header file. It's as comfortable to work with as with separate header file, but it works. The only imports that are left, are import of iOS stuff, such as:
#import <UIKit/UIKit.h>
I'm giving a second answer here with a different approach.
The way Objective C works is, that the compiler needs the full declaration (i.e. the header files) of all the classes that the users of your library will directly call. The library file (the .a file) only contains the compiled code and no declaration. It will only be used by the linker, which is one of the last steps of building an application.
[Programming languages like C or C++ are the same. Programming languages like Java or C# however store meta information about classes in the compiled code so they don't need no header files, just the .jar or .dll file.]
So one approach would be to give the .a and a directory full of header files to your user. They then add the .a file to their project, add a single #import statement wherever they use your classes and add the path to the header file directory to their build settings (it's called Header Search Paths).