SCons / scons

SCons - a software construction tool
http://scons.org
MIT License
2.06k stars 313 forks source link

command str w/ only var name gives python stack trace #804

Closed bdbaddog closed 5 years ago

bdbaddog commented 6 years ago

This issue was originally created at: 2004-04-29 09:46:36. This issue was reported by: garyo.

garyo said at 2004-04-29 09:46:36

I'm getting an error (python stack trace) in a SConscript that seems like it should work.

Here's my SConstruct:

env=Environment()
foo=env.Program('foo.c')
# Below is the problem
env.Command('foo.out', foo, "$SOURCE")    

(doesn't matter what foo.c contains for this bug. Hello, world is fine.)

When run, this gives:

% scons -Q
cl /nologo /c foo.c /Fofoo.obj foo.c
link /nologo /OUT:foo.exe foo.obj
scons: *** Object returned from command generator:
<SCons.Node.FS.EntryProxy instance at 0x00BD2030> cannot be used to create an Action.
scons: internal stack trace:
  File "c:\python23\Lib\site-packages\SCons\Taskmaster.py", line 373, in next_task
    task.make_ready()
  File "c:\python23\Lib\site-packages\SCons\Taskmaster.py", line 180, in make_ready
    if not t.current(t.calculator()):
  File "c:\python23\Lib\site-packages\SCons\Node\FS.py", line 1604, in current
    bsig = calc.bsig(self)
  File "c:\python23\Lib\site-packages\SCons\Sig\__init__.py", line 372, in bsig
    sigs.append(self.module.signature(node.get_executor()))
  File "c:\python23\Lib\site-packages\SCons\Sig\MD5.py", line 85, in signature
    return hexdigest(md5.new(str(gc())).digest())
  File "c:\python23\Lib\site-packages\SCons\Executor.py", line 163, in get_contents
    self.get_build_env())
  File "c:\python23\Lib\site-packages\SCons\Action.py", line 408, in get_contents
    return self.__generate(target, source, env, 1).get_contents(target, source, env, dict=None)
  File "c:\python23\Lib\site-packages\SCons\Action.py", line 369, in __generate
    raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))

If I add a space after $SOURCE in the SConscript like this:

env.Command('foo.out', foo, "$SOURCE ")

it all works fine! I'm guessing the variable expansion code has turned $SOURCE into an FS.EntryProxy rather than a string.

This is all with CVS version, post 0.95.

garyo said at 2004-12-02 14:15:28

Just tried this again with CVS version of 11/22/04 (post 0.96) on Windows, still broken in the same way.

issues@scons said at 2004-12-02 14:15:28

Converted from SourceForge tracker item 944671

bdbaddog said at 2008-03-15 23:36:30

Gary, Here's a test for this:

f = open("foo.c", "w")
f.write("#include <stdio.h>\n")
f.write("main()\n")
f.write('{printf("Helloworld\\n");}\n')
f.close()

env = Environment()
foo = env.Program("foo.c")
env.Command("foo.out", foo, "$SOURCE")

This seems to be fixed, if the above test demonstrates the issue you reported.

garyo said at 2009-01-06 19:26:17

Hi Bill. Actually it doesn't seem fixed to me. The error is different now, but it still never generates foo.out by invoking "foo". At least that's on Linux with SCons 1.2.0 or so. Here's current output from my testcase:

$ scons --debug=explain foo.out
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: building `foo.o' because it doesn't exist
gcc -o foo.o -c foo.c
scons: building `foo' because it doesn't exist
gcc -o foo foo.o
scons: building `foo.out' because it doesn't exist
scons: done building targets.

Note the last two lines. It says it's building foo.out, but then it doesn't do anything. I think that's because $SOURCES is turned into something not a string, so the action it turns into is just nothing.

bdbaddog said at 2009-01-06 23:54:35

Gary,

I just took a look at this. For me since . is not in my path, it shells out "foo" which can't be found by the shell, and thus doesn't get run.

On further investigation,that's also not true, however if I make the following change to my sample SConstruct.

#env.Command('foo.out', foo, "$SOURCE")
env.Command('foo.out', foo, "./$SOURCE")

It does run foo. Also I ran an strace to see if SCons even tries to run it in the original and with the env.PrependENVPath('PATH','.'), and it doesn't.

Here's a new example SConstruct:

f=open('foo.c','w')
f.write('#include <stdio.h>\n')
f.write('main()\n')
f.write('{printf("Helloworld\\n");return(0);}\n')
f.close()

env=Environment()
foo=env.Program('foo.c')
if False:
    env.PrependENVPath('PATH','.')
    env.Command('foo.out', foo, "$SOURCE")
else:
    env.Command('foo.out', foo, "./$SOURCE")

If you run this version, it'll run foo. If you change the False to True, and run it won't run it.

scons foo.out --tree=all --debug=explain --debug=presub

Was useful to see if it was even trying to execute $SOURCE. It only tries when it's ./$SOURCE

Anyone got an idea why this is? I ran with taskmaster trace for both flavors and both outputs match. I'll attach one of them.

bdbaddog said at 2009-01-06 23:55:05

Created an attachment (id=563) new example SConstruct

bdbaddog said at 2009-01-06 23:55:32

Created an attachment (id=564) taskmaster trace file when there's no . in the command

bdbaddog said at 2009-01-06 23:55:51

Created an attachment (id=565) taskmaster trace file when there's a ./ in the command

bdbaddog said at 2009-01-06 23:57:16

I'm cc'ing Greg because I've a hunch it might be taskmaster related.

gregnoel said at 2009-05-13 20:22:41

Bug party triage. Issue #804 and issue #2404 both deal with "lazy actions" where the command evaluation is delayed until later. It may well be that they are dups; at a minimum, they should be worked together.

One possibility is that the problem occurs when the action is evaluated to make the command an implicit dependency. And Steven sez, "... maybe we're tacking on the suffix and then doing the substitution, when we should be doing it in the opposite order for this case."

Has at least two workarounds, so not a high priority. Assigning to 'research' since we don't know what's going on; as soon as you do, bring it back to be re-triaged by setting the milestone back to '-unspecified-'.

gregnoel said at 2010-07-21 17:00:31

Bug party triage. Bump the priority of this issue.

dirkbaechle said at 2010-12-07 17:07:07

Hi there!

I spent some time on debugging this issue and can reproduce the error on my Ubuntu 10.10 machine (64bit) with the latest source from trunk (all following line numbers, stack traces and source snippets refer to rev5189). "Error" here refers to the behavior as reported by Gary, where SCons claims that it starts to build things...but nothing happens.

The obvious symptom of this problem is, that while trying to execute "foo" in

  process [Action.py:700]
  strfunction [Action.py:731]
  __call__ [Action.py:547]
  __call__ [Action.py:890]
  __call__ [Action.py:958]
  do_execute [Executor.py:340]
  __call__ [Executor.py:358]
  build [__init__.py:372]
  execute [Taskmaster.py:237]
  execute [Main.py:183]
  start [Job.py:201]
  run [Job.py:111]
  _build_targets [Main.py:1260]
  _main [Main.py:1066]
  _exec_main [Main.py:1302]
  main [Main.py:1338]

the variable self.cmd_list is empty for the build action in question (with target="foo.out" and source="foo"). So, the call to subst_list()

        if executor:
            result = env.subst_list(self.cmd_list, 0, executor=executor)

returns an empty list, and nothing happens in the following.

At the call of Command() during parsing of the SConstruct file, the $SOURCE is recognized as a variable-only command and initialized to a LazyAction (in contrast to ./$SOURCE). Here, the self.cmd_list is properly set to ${SOURCE}...but where does this information get lost?

At

  do_execute [Executor.py:340]
  __call__ [Executor.py:358]
  build [__init__.py:372]
  execute [Taskmaster.py:237]
  execute [Main.py:183]
  start [Job.py:201]
  run [Job.py:111]
  _build_targets [Main.py:1260]
  _main [Main.py:1066]
  _exec_main [Main.py:1302]
  main [Main.py:1338]

the code (Executor.py: l. 337)

        for act in self.get_action_list():
            #args = (self.get_all_targets(), self.get_all_sources(), env)
            args = ([], [], env)
            status = act(*args, **kw)
            if isinstance(status, SCons.Errors.BuildError):
                status.executor = self
                raise status

gets called with the LazyAction for ${SOURCE} as the only entry in self.action_list. On the call act(*args, **kw) we get to

  __call__ [Action.py:890]
  __call__ [Action.py:958]
  do_execute [Executor.py:340]
  __call__ [Executor.py:358]
  build [__init__.py:372]
  execute [Taskmaster.py:237]
  execute [Main.py:183]
  start [Job.py:201]
  run [Job.py:111]
  _build_targets [Main.py:1260]
  _main [Main.py:1066]
  _exec_main [Main.py:1302]
  main [Main.py:1338]

where CommandGeneratorAction.call (Action.py: l. 882)

    def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
                 show=_null, execute=_null, chdir=_null, executor=None):
        act = self._generate(target, source, env, 0, executor)
        if act is None:
            raise UserError("While building `%s': "
                            "Cannot deduce file extension from source files: %s"
                % (repr(list(map(str, target))), repr(list(map(str, source)))))
        return act(target, source, env, exitstatfunc, presub,
                   show, execute, chdir, executor)

is called and the _generate() returns a CommandAction (!) "act", which doesn't have the self.cmd_list anymore! Well, it is there...but empty.

Now I don't know why this "upcast" (LazyAction->CommandAction) is performed or how the above problem could get fixed (is the multiple inheritance hierarchy for LazyAction a (diamond) problem?). But this is what happens at the moment, I hope my findings help you further.

I also tried to reproduce bug #2404, but it works fine for me under Linux (using the tools "gcc, link"). From my perspective, the two issues don't seem to be directly connected (no dup)...

Best regards,

Dirk

carandraug said at 2013-02-28 17:58:53

Created an attachment (id=915) very simple test case

carandraug said at 2013-02-28 18:09:32

I can still confirm this issue with scons 2.1.0 (Debian Wheezy). A simple

Command("result", "script.pl", "$SOURCE")

Will fail as in nothing is done. SCons recognizes it as a target to be built (checked with --debug=explain) but then simply does nothing about it. I have attached a very simple test case where I have 3 targets using Command which are simple perl scripts (1 is an "hello world", the other should give different errors. Just checking maybe the scripts were being ran but STDOUt was maybe being redirected).

$ scons --debug=explain analysis
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: building `result1' because it doesn't exist
scons: building `result2' because it doesn't exist
scons: building `result3' because it doesn't exist
scons: done building targets.

I'm aware that just using a script at the root of scons would also not work on bash, so I placed them in a directory so that way, even bash would know what to do. Still nothing.

Using ${SOURCE.abspath} as target seems to work fine in the mean time.

bdbaddog attached SConstruct at 2009-01-06 23:55:05.

new example SConstruct

bdbaddog attached no_dot.taskmastertrace at 2009-01-06 23:55:32.

taskmaster trace file when there's no . in the command

bdbaddog attached with_dot.taskmastertrace at 2009-01-06 23:55:51.

taskmaster trace file when there's a ./ in the command

carandraug attached scons-check.tar.gz at 2013-02-28 17:58:53.

very simple test case

bdbaddog commented 5 years ago

I cannot reproduce on mac or linux with examples. I'm guessing we fixed this somewhere along the way. Please reopen if it's still failing for anyone.