How can I add per-file defines to a scons project - scons

I'm in the process of porting a makefile project to scons and I can't figure out how to create a unique #define for each file. I would like to have the base filename for each file defined in order to support some custom debug macros. In the makefile, I'm able to do this with the following definition.
-DBASE_FILE_NAME=\"$(<F)\"
I'm not sure how to do this or if it is even possible in scons and would appreciate any feedback.

After some experimentation, the following seems to work.
import os
from glob import glob
# use Python glob, not scons Glob!
CPP_FILES = glob('./src/*.cpp')
env = Environment(CPPPATH='./include', etc...)
for f in CPP_FILES:
env.Object(f, CPPDEFINES={'BASE_FILENAME' : "\\\"" + os.path.basename(f) + "\\\""})
O_FILES = [os.path.splitext(f)[0] + '.o' for f in CPP_FILES]
env.Program('myprogram', O_FILES)
This lets me define things on a per-file basis without listing the files out individually.

Perhaps the following? (Haven't tried it, but something along those lines should work)
env.Program('filename.c',CPPDEFINES='-DBASE_FILE_NAME=\"$SOURCE\"')

Related

What is the python equivalent to the "*" list all command?

What character do I use similar to "*" in other languages to list/see all of the files, where it doesn't matter what the beginning part is but they all end if say .traj.
So like I want to list the following files from a directory with the following file names
test1. exe
test1.traj
test2.exe
test2.traj
test3.exe
test3.traj
where I want to only list:
test1.traj
test2.traj
test3.traj
in other languages I might do:
print, *.traj
Assuming this is Python3 as that's what you have tagged.
Use the glob module
Like so:
files = glob.glob('*.traj')
for f in files:
print(f)

What does it mean to invoke `make -f` with a target that appears to be setting a variable? (And why isn't it working for me?)

Summary
I am trying to understand a complicated chain of Makefiles, in order to get a build to succeed. I narrowed down my problem to this bit in our build script:
INF_RL=`make -f $BUILD_ROOT/Makefile BUILD_ROOT_MAKEFILE= show__BUILD_INF_RL`
$INF_RL/$BUILD_UTILS_RELDIR/BuildAll.sh
$INF_RL is being set to an empty string (or not being set). If I replace the first line with
INF_RL=/foo_rel_linx86/infrastructure_release/v8.0.14
in order to hardcode what I know $INF_RL is supposed to be, then the build goes smoothly. But I want to know how to fix this the proper way.
What I've Tried / Thought
My first thought was that make -f is failing. So I tried it in my shell:
% make -f $BUILD_ROOT/Makefile BUILD_ROOT_MAKEFILE= show__BUILD_INF_RL
% setenv | grep BUILD_ROOT
BUILD_ROOT=/userhome/andrew.cheong/TPS
Indeed, it returned an empty string. But what conclusion could I draw from this? I wasn't sure if the shell was the same thing as the environment / scope in which Make was chaining together its Makefiles. I abandoned this investigation.
Next, I looked into show__BUILD_INF_RL, which seemed to be defined in $BUILD_ROOT/Makefile:
BUILD_ROOT_MAKEFILE = 1
MAKE_DIRS = src
CASE_KITS = tpsIn tpsOut
REQUIRED_VERSIONS = "case.v$(INF_VS)"
all:
## These next 3 rules allows any variable set in this makefile (and therefore
## the included makefile.include to have it's value echoed from the command
## "make show_<variableName>"
## NOTE: the "disp" target is vital as it allows the show_% implicit rule to be
## recognised as such - implicit rules *must* have a target.
show_% := DISPLAY_MACRO = $(#:show_%=%)
show_% : disp
# echo $($(DISPLAY_MACRO))
disp:
include $(BUILD_ROOT)/makefile.include
Here, I faced more questions:
What is BUILD_ROOT_MAKEFILE for? Why is it set to 1, then seemingly something else in the make -f command?
In the make -f command, is BUILD_ROOT_MAKEFILE= its own argument? If so, what kind of target or rule is that? Otherwise, why is it being set to the macro?
In $BUILD_ROOT, there is another file, makefile.LINUX_X86.include:
BUILD_INF_RL = /foo_rel_linx86/infrastructure_release/v$(INF_VS)
$(warning $(BUILD_INF_RL))
BUILD_UTILS = $(BUILD_INF_RL)/build-utils_LINUX_X86
Though a completely ignorant guess, I think BUILD_INF_RL is being set here, and intended to be extracted into the build script's variable INF_RL when the macro show__BUILD_INF_RL is invoked. I added the middle line to see if it was indeed being set, and indeed, I get this output when running the build script:
/userhome/andrew.cheong/TPS/makefile.LINUX_X86.include:3: /foo_rel_linx86/infrastructure_release/v8.0.14
i.e. Looks like what I've hardcoded way above! But why doesn't it make it into INF_RL? There is yet another file, makefile.include, also in $BUILD_ROOT:
#
# INCLUDE THIS FILE AS THE LAST LINE IN THE LOCAL MAKEFILE
#
# makefile.include - use this file to define global build settings
# e.g. infrastructure version and location, or third-party
#
# supported macros in addition to build-utils-makefile.include
#
# BUILD_INF_RL : optional, specification of infrastructure release location
# defaults to vdev_build area
#
include $(BUILD_ROOT)/../../makefile.include.$(BUILD_ARCH).Versions
#include $(BUILD_UTILS)/makefile.archdef.include
include $(BUILD_ROOT)/makefile.$(BUILD_ARCH).include
$(warning $(BUILD_INF_RL))
_BUILD_INF_RL = $(BUILD_INF_RL)
# place the results at the root of the infdemo tree
BUILD_DEST = $(BUILD_ROOT)
INCLUDE_DIRS += $(BUILD_INF_RL)/core/$(BUILD_TARGET)/include
LINK_DIRS += $(BUILD_INF_RL)/core/$(BUILD_TARGET)/lib
# libraries required for a typical fidessa app, including OA and DB access
FIDEVMAPP_LIBS = FidApp FidInf FidCore Fidevm
include $(BUILD_UTILS)/makefile.include
That $(warning ...) is again mine, and when running the build script, I get:
/userhome/andrew.cheong/TPS/makefile.include:18: /foo_rel_linx86/infrastructure_release/v8.0.14
The Question
The fact that both $(warning ...)s show up when I run the build script that's calling the make -f ... show__BUILD_INF_RL, tells me that those Makefiles are being included. Then what is causing the macro to fail and return an empty string instead of the correct INF_RL path?
Historical Notes
These build scripts were written at a time when we were only compiling for Solaris. (The scripts were based on templates written by an infrastructure team that loosely accounted for both Solaris and Linux, but we never ran the Linux branch, as it was unnecessary.) We are now fully migrating to Linux, and hitting this issue. The reason I'm skeptical of it being a Linux versus Solaris issue is that we have at least four other products that use a similar Makefile chain and have been migrated with no issues. Not sure why this one in particular is behaving different.
Your question got very long and complex so I didn't read it all... for SO it's often better if you just ask a specific targeted question that you want to know the answer to, with a simple repro case.
I can't say why different makefiles behave differently, but this line:
show_% := DISPLAY_MACRO = $(#:show_%=%)
seems really wrong to me. This is (a) setting the variable show_%, which don't actually use anywhere, (b) to the simply expanded string DISPLAY_MACRO = because at this point in the makefile the variable $# is not set to any value.
Maybe you wanted this line to be this instead:
show_% : DISPLAY_MACRO = $(#:show_%=%)
(note : not :=) so that it's a pattern-specific variable assignment, not a simple variable assignment?

Generating ctags for Haskell Platform (standard library), specifically for prelude

I've installed Haskell on my Mac using Homebrew, that is brew install ghc haskell-platform.
I'm looking for a way to generate a ctags file of the standard Haskell Platform libraries (modules) so I could browse the source while coding in Vim. I specifically need Prelude and the other most popular modules, like Data.List and such.
I am aware that the source is available on the web via Hoogle, but It'll be easier for me to jump-to-source whenever I need to, for learning purposes.
Where is the source located when installing the Haskell Platform?
Is the source even installed when installing the Haskell Platform, or just the compiled binaries or something of the sort?
How can I make the source available for browsing in Vim? As in put the generated tags file somewhere and tell Vim to read from it. I also understand there's no need to re-generate the tags file, since these modules are pretty much static and don't get updated very often.
1) and 2) were answered by permeakra in comments. I'll try to cover 3) by describing setup similar to the one I'm using. First simple solution for base libraries, then more generic solution for whatever Haskell source package in general.
As a prerequisites we will need a tool which generates tags file for Haskell:
cabal install hothasktags
Instead of hothasktags you might use your favourite one. See for example https://github.com/bitc/lushtags page which enumerates some of these.
Then we need to have sources for base libraries available. Here I'm using the ones from GitHub:
cd /space/haskell/sources/ # tweak to your personal taste
git clone https://github.com/ghc/packages-base.git
Optionally we might switch to particular branch. E.g.:
git checkout ghc-7.4
Run git branch -a to see all possibilities.
Now let's generate tags for the base libraries (I do not have Mac available and thus have to assume the command works there or you are able to tweak it appropriately):
cd packages-base
export LC_ALL=C # needed for case-sensitive searching
find -type f | egrep \.hs$\|\.lhs$ | xargs -Ii hothasktags i | sort > tags
(Note about sort: My Vim complains when I do not use the sort. For LC_ALL explanation see for example this blog post)
Now we need to let the Vim know about the tags we generated. The easiest way is probably to put the following line into your $HOME/.vimrc:
autocmd FileType haskell setlocal tags+=/space/haskell/sources/packages-base/tags
This way the tags for base libraries will be set for each Haskell file we open. If this is not desirable we can put following Vim command into .vimrc:
autocmd FileType haskell command! SetGHCTags
\ setlocal tags+=/space/haskell/sources/packages-base/tags
and call :SetGHCTags on demand.
For more generic solution which works with all Haskell sources packages we can use the following function (put into .vimrc or into Vim file dedicated to Haskell filetype):
" Add 'tags' of the given package to the current tag stack. The package sources
" must be available in "/space/haskell/sources/<package>" and the tags must be
" generated for it.
fun! s:SetHaskellTags(pathInHaskellSrcDir) "{{{
let tagFile = "/space/haskell/sources/" . a:pathInHaskellSrcDir . "/tags"
if filereadable(tagFile)
exe "setlocal tags+=" . tagFile
else
echoerr "File does not exist or is not readable: " . tagFile
endif
endfunction "}}}
command! -nargs=1 SetHaskellTags call <SID>SetHaskellTags(<args>)
Utilizing it for example for Shelly.hs library:
cd /space/haskell/sources/
git clone https://github.com/yesodweb/Shelly.hs.git
cd Shelly.hs
regenerate-haskell-tags # [1]
In Vim just call:
:SetHaskellTags "Shelly.hs"
There is space for improvement - SetHaskellTags could generate tags if not exist, or could even fetch the sources, configurable Haskell source code storage, directory completion, etc. But works good enough for me now. So at least sharing the solution I have. Will come back here if I get to some of these improvement done.
[1]: It's better to store regenerate-haskell-tags in your $PAHT.

Add SCons files to ack searches

I love the code search utility ack. It is smart enough to look through Makefiles, but doesn't know about the SConstruct and SConscript files that scons uses. How do I add those to the files that ack will look in?
Here is a patch that treats SCons files like make files:
--- ~/bin/ack-old 2011-06-01 15:43:51.000000000 -0600
+++ ~/bin/ack 2011-06-01 15:42:09.000000000 -0600
## -1583,6 +1583,8 ##
return 'skipped' unless is_searchable( $basename );
+ return ('python',TEXT) if $basename eq 'SConstruct' || $basename eq 'SConscript';
+
my $lc_basename = lc $basename;
return ('make',TEXT) if $lc_basename eq 'makefile' || $lc_basename eq 'gnumakefile';
return ('rake','ruby',TEXT) if $lc_basename eq 'rakefile';
This can't be done using ack's type sets. Makefiles and Rakefiles are hard-coded in the source. I thought you could add a scons type by modifying $HOME/.ackrc and adding --type-set=scons=SConstruct,SConscript, but that will search for a file that ends in ".SConstruct" or ".SConscript".
The easiest workaround is to add the -a (all file types) flag to ack.
If you just want ack to search and be able to filter the SConstruct somehow, then you could add #!/usr/bin/python as the first line of the SConstruct file. Ack will then treat the file as python source code, and you can filter with --python.
A new ack2 is in development which will allow exact file matching in the .ackrc file. That will enable easier support for Scons and Jam.

what triggers scons to build files when I have a custom builder?

I'm going nuts trying to control when files are built in scons. I have a very simple example build tree (see below), with a Poem builder that just takes a .txt file and converts it to lower case in a corresponding .eectxt file.
In my SConstruct and SConscript files, I declare dependencies of 3 .txt files.
But I can't figure out what's putting these into the default build!
sconstest/
SConstruct
tiger.txt
src/
SConscript
hope.txt
jabberwocky.txt
where the *.txt files are poems and my SConstruct and SConscript look like this:
SConstruct:
env = Environment();
def eecummings(target, source, env):
if (len(target) == 1 and len(source) == 1):
with open(str(source[0]), 'r') as fin:
with open(str(target[0]), 'w') as fout:
for line in fin:
fout.write(line.lower());
return None
env['BUILDERS']['Poem'] = Builder(action=eecummings, suffix='.eectxt', src_suffix='.txt');
Export('env');
poems = SConscript('src/SConscript');
tigerPoem = env.Poem('tiger.txt');
src/SConscript:
Import('env');
input = ['jabberwocky.txt', 'hope.txt'];
output = [env.Poem(x) for x in input];
Return('output');
What I want to do is to declare the dependency of the .eectxt files from the corresponding .txt files, but not cause them to be built unless I explicitly put them into the Default() build in the SConstruct file, or I request them explicitly at the command line.
How can I do this?
By default, a directory depends on all files and/or targets which reside in it.
So running:
scons
Will then build all targets under the current directory.
I figured out how to do what I want, but I still don't understand why I need to do it this way. Acceptance to the first decent answer that explains it.
Here's what works, if I add the following to the root SConstruct file:
env.Ignore('.', tigerPoem);
env.Ignore('src', poems);
env.Alias('poems', [tigerPoem]+poems);
This ignores the 3 poems from the default target, and then adds them as targets aliased to "poems", so if I run scons it builds nothing, but if I run scons poems it builds the files.
Why does this work? Why does calling env.Poem(...) add something to the default targets?

Resources