SCons / scons

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

Linker error 2011 using PCH with Program() with source files #2680

Open bdbaddog opened 6 years ago

bdbaddog commented 6 years ago

This issue was originally created at: 2010-08-25 15:51:12. This issue was reported by: jpettiss.

jpettiss said at 2010-08-25 15:51:12

Compiling and linking in one step using Program() is painless and intuitive. Attempting to use a precompiled header to speed this up almost works flawlessly, but there are two hacks currently needed to make this work.

Firstly, if the source supplied to the Program() step contains the PCH source itself (typically StdAfx.cpp, but in my example to make it clear I used 'master' for the name of my precompiled header), then scons fails with message:

scons: *** Two environments with different actions were specified for the same target: src\master.obj
File "C:\bwaysource\active\fark\SConscript", line 20, in <module>

Removing the source file from the list of source given to Program() fixes this. If debugging symbols are turned on, however, a linker error is issued:

alpha.obj : error LNK2011: precompiled object not linked in; image may not run output\main.exe : fatal error LNK1120: 1 unresolved externals
scons: *** [output\main.exe] Error 1120

This is because the linker step contains all the object files except the object file associated with the PCH (here, master.obj).

So secondly, we need to tweak the LINKFLAGS to re-add that object:

(client_pch,client_pch_obj)=env.PCH('src/master.cc')
env.Append(LINKFLAGS=Dir(env['OUT_DIR']).File(client_pch_obj))

According to http://msdn.microsoft.com/en-us/library/3ay26wa2%28VS.80%29.aspx [dead link], it's always required that this object file be included (but in fact it only fails when debugging symbols are enabled).

I'll attach a repro with explanations.

jpettiss said at 2010-08-25 15:52:17 Created an attachment (id=790)

Simple repro with explanatory comments

garyo said at 2010-09-04 07:00:05

Hi Jason; thanks for the report. What's your recommended fix? Should SCons always automatically remove the PCH source from the compiler source list and re-add it to the link sources? If you have an idea for a patch that would work in the general case, please let us know.

jpettiss said at 2010-09-07 17:11:04

In practice it turns out there are slightly different tricks necessary to get PCH working with static libraries vs. programs. Boiling it down as far as I can, the difference looks like this:

src = Glob("src/*.cc")
src.remove(File("src/master.cc"))

if no_pch:
    lib = env.StaticLibrary("libname", src)
else:
    env_c = env.Clone()
    env_c.Append(CXXFLAGS="-Yllibname")
    (pch, pch_obj) = env_c.PCH("src/master.cc")
    lib = env_c.StaticLibrary("libname", src + [pch_obj], PCHSTOP="master.h", PCH=pch)

if no_pch:
    exe = env.Program("exename", src, LIBS=libs)
else:
    env_c = env.Clone()
    (pch, pch_obj) = env_c.PCH("src/master.cc")
    exe = env.Program("exename", src, LIBS=libs, PCHSTOP="master.h", PCH=pch)

This assumes that src/ contains all source, including a file master.h and master.cc which only includes master.h, and that all modules #include "master.h the first thing they do.

The -Yl flag seems necessary when building a library, to prevent any objects which don't refer to any symbols in the PCH from generating linker errors. I don't quite get that but it does the trick, documented here:

http://msdn.microsoft.com/en-us/library/w6y1zk9f%28v=VS.71%29.aspx [dead link]

The addition of pch_obj to the source for Program doesn't seem necessary (or at least I haven't yet found a case where not having it caused a linker error). However, adding it certainly doesn't cause any problems.

So far the above works on all release configurations I can think of under VS2005 and VS2008.

I'm not sure what's easiest in terms of an internal fix; seems like the source node created/given to PCH needs to be stored somewhere, since the Program/StaticLibrary step only take .pch file.

But having PCH return a foursome might be a cleaner way to deal with things:

lib = env.Library('libname', src,  PCH=env.PCH('src/master.cc', 'master.h'))

It could return the pch file, the pch object file, the source node, and the PCHSTOP name, such that the PCH argument to Library/Program would have all the information necessary to make this work regardless of the compiler version.

Conditional support for PCH would be a lot easier this way too, by just adding a dummy PCH builder which returns an empty list.

gregnoel said at 2010-09-27 17:35:55

Bug party triage.

jpettiss attached issue2680-repro.zip at 2010-08-25 15:52:17.

Simple repro with explanatory comments

mwichmann commented 3 years ago

The link to MS docs has unsurprisingly gone stale, this seems to describe the option mentioned: https://docs.microsoft.com/en-us/cpp/build/reference/yl-inject-pch-reference-for-debug-library?view=msvc-160