[CMake] lexical scoping ... back to the future !
François Mauger
mauger at lpccaen.in2p3.fr
Thu Feb 23 19:10:42 EST 2012
>>> On 23/02/2012 02:00, Jean-Christophe Fillion-Robin wrote:
> Hi François,
>
> Would the use of function be helpful ?
>
> See http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:function
> and http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set
>
> "If PARENT_SCOPE is present, the variable will be set in the scope above
> the current scope. Each new directory or function creates a new scope.
> This command will set the value of a variable into the parent directory
> or calling function (whichever is applicable to the case at hand)."
>
> I created a small example showing that variable set within a function
> don't interfere with the parent scope if not explicitly required.
>
> git clone git://gist.github.com/1888836.git
> <http://gist.github.com/1888836.git>
> cmake -P 1888836/cmake-illustrate-function-scope.cmake
>
Hi JC
I've also played around with functions vs macros to check for scoping
features and I have basically written the same test script for my own
learning. I agree that SET coupled with the PARENT_SCOPE directive can
solve some scope issues within function, preserving the very useful
ability to have "pure" local variables and still making possible for a
given variable to be visible ONE scope level up. The problem is when you
need to invoke several find_package commands from a thread of nested
function/macros, eventually with some recursive pattern.
AFAIU, a find_package() command implies to be invoked from a macro, and
not a function, to preserve the global scoping of the xxx_LIBRARIES and
xxx_INCLUDE_DIRS variables set from a FindXxx.cmake or xxx-config.cmake
file, and thus make them visible at the top-level CMakeLists.txt file
for example.
May be I'm wrong and I miss something but it seems other people, trying
to setup a set of homemade macros/functions to traverse automatically a
dependency tree, have also faced this kind of scoping issue.
Because I have to build some complex applications and libraries from
a large set of "low-level" dedicated home-made libraries that have
dependency links between them (not cyclic of course, but possibly
diamond shaped), I've implemented a basic CMake dependency driver that
uses a simple lists of dependency rules for any new home-made library
(let's call it 'xxx') that depends on some other libraries I have at hand.
from the xxx's top-level CMakeLists.txt, I just have to set:
{{{
SET( xxx_deprules "GSL:>=1.12;Python:>=2.6:<=2.7.3;foo:>=2.1;bar:=3.2" )
}}}
which means I want to check and use:
- GSL libs with ver >=1.12
- Python (interp+libs) with ver in [2.6-2.7.3]
- my home-made foo lib with ver >=2.1
- my home-made bar with ver==3.2
and then automatically build some xxx_INCLUDE_DIRS and xxx_LIBRARIES
from these rules.
There is a special set of macros that are given the "xxx_deprules" list
and are supposed to invoke the 'find_package (...)' command to check if
the dependencies in the list are fulfilled. Then the macros
collect/aggregate the various [GSL|Python|foo|bar]_INCLUDE_DIRS and
[GSL|Python|foo|bar]_LIBRARIES variables. Note that packages like GSL
and Python or Boost use typically the so-called 'Module' find_package
mode (FindGSL, FindPythonInterp, FindBoost...)
while my foo and bar package uses the 'Config' mode through
well-formatted foo-config.cmake and bar-config.cmake files.
Note also that because the FindXxx.cmake files provided with the CMake
distribution does not follow a strict pattern and standard (variable
names...), I have to wrap the find Module within some dedicated macros
to standardize the output (examples: Boost_VERSION gives "104700" and
not "1.47.0", argh ! ).
So far, so good. Now assume that the "foo" lib depends also on external
libs, say : 'john' and 'doe'. because they are also home-made libraries,
I also provide some 'john-config.cmake' and 'doe-config.cmake' files
for both.
The dependency check for the 'xxx' lib is implemented in the following way:
0) From the xxx Top-level CmakeLists.txt file, I loop on the list of
dependecy rules (gsl, python, foo...)
1 ) Example for "foo:>=2.1" : invoke
{{{
find_package (foo 2.1 REQUIRED NO_MODULE HINT ...)
}}}
this loads the 'foo-config.cmake' file with all
its exported DLL targets : here the john DLL and the doe DLL
2) because foo has been build with john==1.2 and doe==3.1.4, the
foo-config.cmake calls explicitely :
{{{
find_package (john 1.2 EXACT REQUIRED NO_MODULE HINT ...)
}}}
and
{{{
find_package (doe 3.1.4 EXACT REQUIRED NO_MODULE HINT ...)
}}}
with the consequence that both john-config.cmake and doe-config.cmake
will be sourced with their own exported targets.
3) again if john also depends on other libs (exported targets), the
process recurses down to the leaf dependency
the result is that, given a simple dependency rule
in the top-level cmake file :
"GSL:>=1.12 ; Python:>=2.6:<=2.7.3 ; foo:>=2.1 ; bar:=3.2"
we can rebuild the full path of the
depencency tree, including hidden parent dependencies exported target,
and store it in some global variables to be used
with the include_directories(...) and target_link_libraries(...) commands.
this system is based on a strict design pattern I use for my home-made
library packages with a standard interface (names of the variables,
location of various .cmake files...) and a set of smart *macros* that
drive the recursion, preserving the global scope of all
dependency-related variables and exported targets. The main issue comes
from the fact that to write such macros and algos, one needs to use many
temporary variables with dynamic naming such as :
{{{
foreach (depname ${depnames})
set ( ${depname}_DIR ...)
...
}}}
within foreach loops or if/endif tests, also to tokenize some variables
and locally parse directives stored in string variables.
Without local scoping, this turns to be a mess when you meet a
dependency recursion with at least two levels (variable names collision,
corruption of variables in the parent scope due to reuse of a variable
name in a daughter macro...).
Maybe there is another way in the CMake language to design such a
dep-driver without "local scoping". From my programing experience
with various languages such as C/C++/Java/Python/shell, local scoping
is a natural way to achieve this without significant issues. In short,
it is a fundamental tool for most language. Here CMake lacks this
feature and makes life harder (at least mine) !
Even if I've found a solution to workaround this Cmake behaviour, I'm
still interested in this general question :
" Will Cmake implement some kind of "local scoping" in the future ? "
;-)
Apologize for this long thread, but I hope it will help you to figure
out the pb I have faced.
cheers
frc
--
> Hth
> Jc
>
> On Wed, Feb 22, 2012 at 7:07 PM, François Mauger
> <mauger at lpccaen.in2p3.fr <mailto:mauger at lpccaen.in2p3.fr>> wrote:
>
> Hi CMakers,
>
> In november 2007, there was a long thread titled "lexical scoping".
> The discussion was really interesting and was suggesting that some
> "local scoping" features for variables could be implemented in CMake...
> particularly from macros, which are probably the practical case that
> causes most issues when users implement several nested levels of
> macro invocations with many intermediate temporary variables that
> unfortunately aggregate in the global scope. It is really easy to
> face such problem in a rather large project driven with Cmake (a
> dependency driver invoking the find_package command in a foreach
> loop must be done from a macro AFAIK).
> Unless I missed something in the jungle of CMake threads and doc,
> I'm afraid I was not able to find some satisfactory solution other
> than hacks (using very long variable names to "emulate" some pseudo
> local scopes). this is tedious and bug prone.
>
> Browsing the last 4 years of archives and some additionnal docs, I
> was not able to find a single trace of such new feature :
> implementing "local scoping" in CMake (whatever "local scoping"
> could rationally mean).
>
> What is the current situation on users' side ? Is there any pressure
> for "local scoping" ? Maybe too few people ask for it so CMake
> developpers do not consider it as a priority... or no clear definition
> of "local scoping" has been patterned ?
> more what is the view of the CMake devel team ? do they consider
> this idea as a good one or not ?
>
> Thanks a lot for hints and advices.
>
> Best regards
> frc
> --
> François Mauger
> LPC Caen-CNRS/IN2P3-UCBN-ENSICAEN
> Département de Physique -- Université de Caen Basse-Normandie
>
> --
>
> Powered by www.kitware.com <http://www.kitware.com>
>
> Visit other Kitware open-source projects at
> http://www.kitware.com/__opensource/opensource.html
> <http://www.kitware.com/opensource/opensource.html>
>
> Please keep messages on-topic and check the CMake FAQ at:
> http://www.cmake.org/Wiki/__CMake_FAQ
> <http://www.cmake.org/Wiki/CMake_FAQ>
>
> Follow this link to subscribe/unsubscribe:
> http://www.cmake.org/mailman/__listinfo/cmake
> <http://www.cmake.org/mailman/listinfo/cmake>
>
>
>
>
> --
> +1 919 869 8849
>
--
François Mauger
Groupe "Interactions Fondamentales et Nature du Neutrino"
NEMO-3/SuperNEMO Collaboration
LPC Caen-CNRS/IN2P3-UCBN-ENSICAEN
Département de Physique -- Université de Caen Basse-Normandie
Adresse/address:
Laboratoire de Physique Corpusculaire de Caen (UMR 6534)
ENSICAEN
6, Boulevard du Marechal Juin
14050 CAEN Cedex
FRANCE
Courriel/e-mail: mauger at lpccaen.in2p3.fr
Tél./phone: 02 31 45 25 12 / (+33) 2 31 45 25 12
Fax: 02 31 45 25 49 / (+33) 2 31 45 25 49
More information about the CMake
mailing list