CMake: Access variables across sub directories - cmake

I have a two sub directories from root on which one has the line:
set(${LIBNAME}_publicheaders
LocalizeResource.h
)
I want to be able to access this variable from the other subdirectory. How can I do this?

#JoakimGebart's answer is probably the more common way to go about this. However, you can also use get_directory_property directly from within the second subdir to achieve what you're after.
I see that in your comment, you've used ${LIB_NAME}_publicheaders, but in your question you have ${LIBNAME}_publicheaders. This could be the cause of your problems, since the command should work like this:
get_directory_property(MyVar
DIRECTORY ${CMAKE_SOURCE_DIR}/abc
DEFINITION ${LIBNAME}_publicheaders)
However, there are a couple of provisos:
This has to be called after setting the variable in the subdir. i.e. you'd have to ensure add_subdirectory(abc) was called before the add_subdirectory for the one where this will be used.
If LIBNAME is also set inside the same subdir (abc), you'll need to retrieve the value for that first.
So, while this is probably a less common solution, it has the advantage that it doesn't "pollute" the global namespace with subdir-specific variables - this works from with a subdir referring to another subdir.

You can set the variable in the parent scope using the PARENT_SCOPE option to set()
Example:
set(${LIBNAME}_publicheaders
LocalizeResource.h
PARENT_SCOPE
)
See http://www.cmake.org/cmake/help/v2.8.10/cmake.html#command:set
This, however, means that the variable is available not only in the other subdirectory, but in any other subdirectories on the same level as well.

Related

How to set value by code to a CACHE variable defined by 3d party CMake?

In my project, the CMakeLists includes other cmake files from a library and those dependencies need some cache variables to be configured by user values.
It is all working well if I define those values from the command line with the cmake command:
-DTHIRDPARTY_FRAMEWORK_ROOT="$thirdpartyFrameworkPath"
But can I define (= hardcode) such values in my own CMakeLists file?
To avoid my own users to do it when they configure my project (some values of the 3d party configuration are constant in my project), and make my own cmake interface simpler.
I tried to simply set the variable with a value, but it is both defined and used in the included cmake so it gets overwritten with their default value just before being used.
Using set(... FORCE) seems to work but it does not look clean to me, and might lead to confusing errors if they rename or change the type of the variables on their side. It also forces me to add a type and a doc string because of the set(... CACHE ...) syntax.
Is there a better way to do this?
Setting CACHE INTERNAL variable is a proper way for hardcode a parameter of the inner project in the outer one:
set(THIRDPARTY_FRAMEWORK_ROOT CACHE INTERNAL "Hardcoded root for 'thirdparty'" <value>)
INTERNAL type makes sure that this setting will overwrite the option (FORCE doesn't need) and makes sure that the option won't be shown for a "normal" user.
Since the parameter is not intended to be changed by a user, its real type is meaningless, so there is no needs for it to coincide with the one set in the inner project.
As for description, you could set it to be empty (the parameter is not shown to the normal user, remember?). Alternatively, in the description you could explain why do you set the variable in the outer project. So an "advanced" user will see your description.

How do I make Meson object constant?

As explained here, I like to create file objects in subdirs, and library / executables in the top-level file. However, since all the variables end up in global scope, two subdir files could accidentally use the same variable names. For example:
# Top-level meson.build
subdir('src/abc')
subdir('src/def')
# src/abc/meson.build
my_files=files('1.c','2.c')
# src/def/meson.build
my_files=files('3.c','4.c')
I want meson to throw an error when src/def/meson.build tries to assign a value to my_files. Is this possible in Meson 0.50?
Reassigning variables is rather legitimate operation in meson, so it looks as it is not possible to generate error in standard way. One way of avoiding this problem is following some naming rules e.g. according to folders/sub-folders' names (abc_files, def_files in your case).
But if you really need to have variables with the same name and make sure they are not reassigned, you can use is_variable() function which returns true if variable with given name has been assigned. So, place the following assert before each assignment:
assert(not is_variable('my_files'), 'my_files already assigned!!!')
my_files=files('3.c','4.c')

CMake cache variables vs. global properties: Simple syntax to use the variable value

To make values available to the whole CMake environment from within a subdirectory one may set a cache variable using the set(VARIABLE_NAME Value CACHE INTERNAL "") syntax or set a global property using the set_property(GLOBAL PROPERTY VARIABLE_NAME Value) syntax (see also this very good answer about variables in CMake).
Using the latter has the advantages that you are not "polluting" the CMake cache for something it is not designed for and that you are not dependent on the cache being deleted when not using the FORCE parameter.
But the syntax to use the variable value is not that user-friendly as you have to retrieve the value using get_property instead of simply using the ${...} notation.
Is there a simpler syntax to use instead of get_property (some kind of syntactic sugar)?
Let's summarize the comments.
To my actual question: There is no specific shortcut to use get_property.
Useful comments:
As CACHE INTERNAL implies FORCE it is okay to use cached variables to make variables globally accessible.
It is good practice to start the CMake file by explicitly cleaning / setting the internal cache variables to avoid unpredictable behavior at repeated runs.

progruard to only obfuscate variable names?

I would like to use ProGuard to just rename variable and functions. I want the resultant class files to be identical to the source jar in every regard except for the names. Is there an option to do this?
My attempts at using dontoptimize and dontshrink are not working.
Just put the following in your .pro file, and make sure that you don't use the "-keep" option to prevent other things from being obfuscated.
# Don't keep the local variables attributes (LocalVariableTable and LocalVariableTypeTable are dropped).
-keepattributes Exceptions,Signature,Deprecated,SourceFile,SourceDir,LineNumberTable,Synthetic,EnclosingMethod,RuntimeVisibleAnnotations,RuntimeInvisibleAnnotations,RuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations,AnnotationDefault,InnerClasses,*Annotation*
In other words, make sure that you don't have the following in your options
-keepattributes LocalVariableTable,LocalVariableTypeTable

Variable scope and source command in Tcl

I have the following two files:
a.tcl:
set condition false
source b.tcl
b.tcl:
if {$condition} {
puts "hello"
}
When I run a.tcl, it prints "hello". Is this a correct practice for accessing variable defined in a.tcl? What is the scope of $condition in b.tcl? Thank you.
The scope of condition is global. The source command evaluates the script read from the specified file in the context it's run; in your case this context is also global, hence your puts works.
The question about practice is more complicated as it hightly depends on what you actually do.
The way the source command works is pretty much exactly as if it was reading the file into a string and then passing that to eval (the sole subtlety is to do with info script). That means that the scope that the source was done in will be the one that the outermost level of the script is evaluated in, and so that you could have condition be a local variable there:
proc funkystuff {condition} {
source b.tcl
}
funkystuff true
That will work (and is in fact vital for how Tcl's package definition scripts work; they're evaluated in a context where there is a local variable $dir that describes where the package definition is located) but it can most certainly lead to code that is confusing!
Because of this, it's good practice to write your scripts so that the code inside them makes no assumptions about what context it is evaluated in. The easiest way to do that is often to put the code in the script inside a namespace, where the name of the namespace is fully qualified.
namespace eval ::foobar {
# Do stuff here...
}
It's also a good thing to try to write code that isn't excessively parameterized on sourcing, instead saving that for either which version of the code you load (e.g., one file for Linux, another for Windows) or what parameters you pass to the commands. Of course you don't have to work that way, but it does help make your code robust and easy to understand.
Finally, the scope used for the main script to a Tcl interpreter is always evaluated at the global level (i.e., in the :: namespace with no parent scope).