[CMake] lexical scoping ... back to the future !

aaron.meadows at thomsonreuters.com aaron.meadows at thomsonreuters.com
Fri Feb 24 10:22:58 EST 2012


Very fascinating!  I've similarly had to deal with spiraling dependencies in our local code.

I had two thoughts toward the problems you mentioned below.  To handle unique naming of temporaries, you could do something like introduce a counter (probably as a global property) and append it to your variable names.  In psuedocode:
   RecursingMacro()
      # Get Global Property        => oNEST
      # Increment Global Property  => iNEST= NEST+1
      # Set Global Property        <= iNEST

      # Use for uniqueness

      foreach (depname ${depnames})
          set ( ${depname}_DIR_{$iNEST} ...)
      ...

However, you might not need to do that.  You could use functions and then just store your results in global properties, with a list of their names in a known property (continuously append the new variable names).  Then at the end of the recursive dependency walk, you can import those properties into variables at the parent scope.  

Anyway, just a thought..


Aaron Meadows


-----Original Message-----
From: cmake-bounces at cmake.org [mailto:cmake-bounces at cmake.org] On Behalf Of François Mauger
Sent: Thursday, February 23, 2012 6:11 PM
To: Jean-Christophe Fillion-Robin; cmake at cmake.org
Subject: Re: [CMake] lexical scoping ... back to the future !


 >>> 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

--

Powered by www.kitware.com

Visit other Kitware open-source projects at 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

Follow this link to subscribe/unsubscribe:
http://www.cmake.org/mailman/listinfo/cmake

This email was sent to you by Thomson Reuters, the global news and information company. Any views expressed in this message are those of the individual sender, except where the sender specifically states them to be the views of Thomson Reuters.


More information about the CMake mailing list