[Scons-users] Generating header from cpp file causes cycle

Ivan Nedrehagen ivan at nedrehagen.com
Sun Mar 19 21:10:17 EDT 2017


It must be me who is misunderstanding something, I have generated a make 
file to test your assumptions,

and I cannot see the behaviour that you describe.

Make behaves like I want to:

CPP = g++
PY = python
GENERATOR = gen.py
CPPFLAGS = -I.
DEPS = test.h
OBJS = test.o

test.h: test.cpp
     $(PY) $(GENERATOR) $@ $^

test.o: test.cpp test.h
     $(CPP) -c -o $@ $< $(CPPFLAGS)

testexe: test.o
     $(CPP) -o $@ $^ $(CPPFLAGS)

With this make file, I get this behaviour:

$ make testexe

python gen.py test.h test.cpp
generating test.h from test.cpp
g++ -c -o test.o test.cpp -I.
g++ -o testexe test.o -I.

$ touch test.cpp
$ make testexe

python gen.py test.h test.cpp
generating test.h from test.cpp
g++ -c -o test.o test.cpp -I.
g++ -o testexe test.o -I.

$ touch test.h
$ make testexe

g++ -c -o test.o test.cpp -I.
g++ -o testexe test.o -I.

$ touch test.o
$ make testexe

g++ -o testexe test.o -I.

$ touch test.cpp
$ make testexe

python gen.py test.h test.cpp
generating test.h from test.cpp
g++ -c -o test.o test.cpp -I.
g++ -o testexe test.o -I.

The above is the behaviour I would like. Perhaps I am expressing it the 
wrong way in SCons.


You say that if I generate a file which is used in any way in generating 
the file I got a cycle.

But the cpp file is used in generating, not the header

And the header is not needed by the cpp file (it has no depedencies)

If even if this was the case the SCons would say:

test.h -> test.cpp -> test.h

when reporting the cycle


My actual code generator works like this:

c++ headers is largely redundant. Most of the information can be found 
in the cpp file.

For my unittest framework, all information needed are in the cpp file.

The generator takes the cpp file, and generate a simple header from it.


On 2017-03-19 16:30, Bill Deegan wrote:
> If you generate a file which is used in any way in generating the 
> file, then you have a dependency cycle.
>
> SCons will scan the sources and find these.
> If you did this with make it would always rebuild your code generator.
>
> What does your actual code generator do with the header file it 
> includes which it generates?
>
> Is this a version string?
>
> -Bill
>
> On Sun, Mar 19, 2017 at 6:07 AM, Jean-Baptiste Lab 
> <jeanbaptiste.lab at gmail.com <mailto:jeanbaptiste.lab at gmail.com>> wrote:
>
>     I think what is happening behind the scene is that SCons will
>     constantly update the dependencies as it discovers them, to get
>     the most accurate information possible.
>
>     So you are right, when first processing the dependencies for
>     "test", there are no trace of "test.h".
>     But SCons knows already that test depends on test.o which depends
>     on test.cpp (those are *explicit* dependencies).
>     Later on with the Header builder, you add (manually in a sense) to
>     test.h a dependency on test.cpp,
>     which will add the dependencies of test.cpp to test.h, hence
>     test2.h showing up in test.h dependencies...
>
>     That's were the cycle comes from.
>
>     You might want to take a look at SCons's Ignore() functionality
>     and see if you can use it for your use case...
>
>     Hope this helps,
>
>     JB
>
>     On 2017-03-19 10:55, Ivan Nedrehagen via Scons-users wrote:
>>
>>     Hi,
>>
>>     I have printed the tree where I have replaced the #include
>>     "test.h" with "test2.h"
>>
>>     g++ -o test.o -c test.cpp
>>     +-.
>>       +-SConstruct
>>       +-test
>>       | +-test.o
>>       | | +-test.cpp
>>       | | +-test2.h
>>       | | +-/usr/bin/g++
>>       | +-/usr/bin/g++
>>       +-test.cpp
>>       +-test.h
>>       | +-test.cpp
>>       | +-test2.h
>>       +-[test.o]
>>       +-test2.h
>>
>>     Here you can see that test.cpp depends on nothing (it cannot, it
>>     isn't built), however the test.o depends on test.cpp and test2.h
>>     (which is correct)
>>
>>     But somehow the test.h has also got a dependency on test2.h
>>
>>     As I understand SCons, test.cpp should be scanned by the Header
>>     builders scanner
>>
>>     It behave as if the header builder was a object builder with a
>>     standard cpp scanner
>>
>>     So it must be my understanding of the scanners or SCons
>>     dependencies that are wrong.
>>
>>     But there definitely doesn't have to be a cycle here.
>>
>>     If test.h changes test.o must be rebuilt
>>
>>     If test.cpp changes test.h and test.o must be rebuilt
>>
>>
>>     On 2017-03-19 10:11, Jean-Baptiste Lab wrote:
>>>     Hi,
>>>
>>>     SCons is doing the right thing here I believe...
>>>
>>>     > header = env.Header("test.h", "test.cpp")
>>>
>>>     This statements indicates that "test.h" depends on "test.cpp"
>>>     (builders implicitly make their targets dependent on their sources).
>>>
>>>     Then using:
>>>
>>>     > exe = env.Program("test.cpp")
>>>
>>>     you indicate that the "exe" target depends on "test.cpp".
>>>     "test.cpp" will be scanned by SCons for its dependencies, which
>>>     will end up in eventually in "test.cpp" depends on "test.h".
>>>     SCons then realizes that "Oh, I know how to build "test.h" and
>>>     it depends on "test.cpp"" and that's where the dependency cycle
>>>     occurs ("->" in the following indicates "depends on"):
>>>
>>>     exe -> test.o -> test.cpp -> test.h (through SCons scanner) ->
>>>     test.cpp (through env.Header) -> test.h ->... -> test.h -> test.cpp
>>>
>>>     The scenario you describe is: cpp file X is needed to build the
>>>     header Y that will be included in X which is correctly a
>>>     circular dependency... SCons has no way of figuring out when to
>>>     build what:
>>>     - X has changed -> "I need to rebuild Y"
>>>     - wait, Y has changed -> "I need to rebuild X"
>>>     - wash, rinse, repeat
>>>
>>>     The usual way to solve this is to refactor the dependencies,
>>>     maybe using "test.h.in <http://test.h.in>" file and use that as
>>>     the source of you env.Header() builder...
>>>
>>>     Hope this helps,
>>>
>>>     JB
>>>
>>>
>>>     On 2017-03-19 09:51, Ivan Nedrehagen via Scons-users wrote:
>>>>
>>>>     This is a case that reproduce the problem I have.
>>>>
>>>>
>>>>     It is not perfect, but it still illustrates the problem.
>>>>
>>>>
>>>>     the header I generate doesn't include anything. It becomes one
>>>>     line: "class Test {};"
>>>>
>>>>     the test.cpp includes the file I generate of course. But in my
>>>>     understanding that only means that test.o depends on test.cpp
>>>>     and test.h
>>>>
>>>>     This is reflected in the tree.
>>>>
>>>>     For me it looks like that SCons decide that test.h depends on
>>>>     test.cpp, and that I therefore also must depend on test.h
>>>>     (since it is included)
>>>>
>>>>     This is also reflected in the tree (if I changed the #include
>>>>     "test.h" in test.cpp into #include "test2.h", test.h seems to
>>>>     be dependant on test2.h)
>>>>
>>>>
>>>>     Please explain what I am doing wrong...
>>>>
>>>>
>>>>     On 2017-03-19 05:17, Bill Deegan wrote:
>>>>>     Your header file builder includes the header being generated.
>>>>>     There is a dependency cycle..
>>>>>     Is that really the code you have? (or just an example which
>>>>>     doesn't mimic your actual build?)
>>>>>
>>>>>
>>>>>     -Bill
>>>>>
>>>>>     On Sat, Mar 18, 2017 at 10:00 PM, Ivan Nedrehagen via
>>>>>     Scons-users <scons-users at scons.org
>>>>>     <mailto:scons-users at scons.org>> wrote:
>>>>>
>>>>>         I have a unit test library that generates headers from the
>>>>>         cpp files that contains tests.
>>>>>
>>>>>         Lately with my updated version of SCons (2.5.0) I get
>>>>>         dependency cycles.
>>>>>
>>>>>         (It is very hard to debug these cycles, because --tree
>>>>>         doesn't output anything)
>>>>>
>>>>>
>>>>>         A case that reproduce the problem:
>>>>>
>>>>>         --- SConstruct ---
>>>>>
>>>>>         def create_header(target, source, env):
>>>>>             txt = " class Test {}; \n"
>>>>>             file = open(str(target))
>>>>>             file.write(txt)
>>>>>
>>>>>         env = Environment()
>>>>>
>>>>>         headerBuilder = env.Builder(action=create_header)
>>>>>
>>>>>         env.Append(BUILDERS={"Header": headerBuilder})
>>>>>
>>>>>         header = env.Header("test.h", "test.cpp")
>>>>>         exe = env.Program("test.cpp")
>>>>>
>>>>>         --- test.cpp ---
>>>>>
>>>>>         #include "test.h"
>>>>>
>>>>>         int main() {
>>>>>           Test test;
>>>>>           return 0;
>>>>>         }
>>>>>
>>>>>         --- Result ---
>>>>>
>>>>>         scons: *** Found dependency cycle(s):
>>>>>           test.h -> test.h
>>>>>
>>>>>         --- End of Case ---
>>>>>
>>>>>         Why is this failing? I understand that the test.o should
>>>>>         be dependent on test.h, but why is suddenly test.h
>>>>>         depending on itself?
>>>>>
>>>>>         I tried to set target_scanner and source_scanner to None
>>>>>         in the builder just to see if this made a difference, but no.
>>>>>
>>>>>         How can I express a builder that builds header files from
>>>>>         cpp files without creating a builder that build dependency
>>>>>         cycles?
>>>>>
>>>>>         _______________________________________________
>>>>>         Scons-users mailing list
>>>>>         Scons-users at scons.org <mailto:Scons-users at scons.org>
>>>>>         https://pairlist4.pair.net/mailman/listinfo/scons-users
>>>>>         <https://pairlist4.pair.net/mailman/listinfo/scons-users>
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>>     _______________________________________________
>>>>     Scons-users mailing list
>>>>     Scons-users at scons.org <mailto:Scons-users at scons.org>
>>>>     https://pairlist4.pair.net/mailman/listinfo/scons-users
>>>>     <https://pairlist4.pair.net/mailman/listinfo/scons-users>
>>>
>>>     _______________________________________________
>>>     Scons-users mailing list
>>>     Scons-users at scons.org <mailto:Scons-users at scons.org>
>>>     https://pairlist4.pair.net/mailman/listinfo/scons-users
>>>     <https://pairlist4.pair.net/mailman/listinfo/scons-users>
>>
>>     _______________________________________________
>>     Scons-users mailing list
>>     Scons-users at scons.org <mailto:Scons-users at scons.org>
>>     https://pairlist4.pair.net/mailman/listinfo/scons-users
>>     <https://pairlist4.pair.net/mailman/listinfo/scons-users>
>
>     _______________________________________________ Scons-users
>     mailing list Scons-users at scons.org <mailto:Scons-users at scons.org>
>     https://pairlist4.pair.net/mailman/listinfo/scons-users
>     <https://pairlist4.pair.net/mailman/listinfo/scons-users> 
>
> _______________________________________________
> Scons-users mailing list
> Scons-users at scons.org
> https://pairlist4.pair.net/mailman/listinfo/scons-users
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://pairlist4.pair.net/pipermail/scons-users/attachments/20170320/9d6864df/attachment-0001.html>


More information about the Scons-users mailing list