variable scoping confusion..

I am totally confused about the set() command and variable scoping.

I am trying to refactor a number of things into smaller reusable cmake things and when I do - things go wrong - really wrong.

For example - I want to - in my cmake scripts do something like this:

set_target_riscv()
or:
include( common-target-riscv )

I have others I need to support, microblaze and arm-cortex-m4

Today - I have a huge unwieldy CMAKE script that is hundreds of lines of boiler plate code.
I want to refactor this into either “include” files or functions I can call.

And in those functions - I want to set - for lack of a better word: “global variables”

As an example, I have a function I wrote that works like this:

function( noisy_set VARNAME VALUE )
message( STATUS “${VARNAME}: ${VALUE}”)
set( “${VARNAME}” “${VALUE}” PARENT_SCOPE )
endfunction()

I invoke it like this:

 noisy_set( FOOBAR   'some string' )

The variable is not set in the calling script or any other scope

How do I set the variable GLOBALLY?
How do I set a variable LOCALLY?
Is there a means to declare a variable GLOBAL or LOCAL?

My other problem is - of course I am refactoring things so …
FunctionA() - calls functionB() and functionC() … finally C does the work
For example it might call “noisy_set()” - I have no idea how many parent scopes I need.
In TCL - there is a means to set GLOBAL or “upvar” - type things
In python there is a “global” command
I’m looking for something like GLOBA_SCOPE in CMAKE … but it does not seem to exist.

Then when I return to A … the variables et by B, C,D or E - do not exist,“parentscope” is not viable here - I need GLOBAL SCOPE but that does not seem to exist?

And - is there a reasonable debugger for CMAKE? I am resorting to message() far to much
I really would like to be able to step through things so I can better understand this

Scopes in CMake are…complicated.

First, there are two main classes of variables:

  • cached
  • local

Local variables are “normal” in that they disappear when a scope ends (functions, directories). Normal set and things like list() and string() operate on local variables.

Cache variables are global with the following caveats:

  • any defined local variable of the same name will shadow the cache variable
  • set(CACHE) will not change an already-cached variable except when either:
    • the specified type is INTERNAL; or
    • the FORCE argument is specified

You can only set local variables in your current scope or in the immediate parent scope. To pass it through multiple functions, all of them will need to “fire brigade” the value up the stack with PARENT_SCOPE calls.

If you need truly global variables but not cached between configure runs, I recommend using custom global properties instead:

set_property(GLOBAL PROPERTY mypropname value)
get_property(localvar GLOBAL PROPERTY mypropname)

There’s not really a debugger as such. cmake --trace-expand is useful though.

yea, that makes it hard to refactor things.

The goal being - have some module that:

 settings_for_target_type( "COMPANY_RISCV_TYPE_PROJECT" )

Things like compiler defines, linker information, library information, flags, and other things - ie: In my case - some of these are used by the Compiler Linker librarian process, but a number of these are used by secondary tools that are done with Custom Commands.

examples:

  • Note: we cross compile to embedded things. So - we have to “compile in as large data binaries” a number of data tables. {ie: Convert a large BINARY data file into a large byte binary array in the target}
  • We pre-process a number of data files to create or generate C & H files for use in the target (we have more then just simple binary tables)
  • We also post-process the ELF to create a binary loadable image for our targets {we cross compile to embedded things}, examples are creating our “run time binary” - by doing much more then "objdump’
  • We also post process the ELF adding digital signatures to the generated binary, these steps are unique to our product/process (our downloads for our targets are not just HEX files)

It would be great if there was a: SET_GLOBAL_VAR( NAME, VALUE )
That just worked… in a standard way.

If you want to ship flags and such around, I suggest having an INTERFACE target that you use target_compile_options() (and that family of commands) on it. You then use the flags by linking to it with target_link_libraries. CMake will add the flags from that target as needed then.