I have often seen Makefiles that start commands with an "#" symbol to suppress normal output.
target: test
#echo foo
Has this output:
$ make test
foo
But I often encounter Makefiles with ## in front of commands:
target: test
##echo foo
And the output is identical, as far as I can tell, from Makefiles with only one # before the echo command.
What's the difference?
(The ## seems to be common practice, as seen by this Google Code search: http://www.google.com/codesearch#search/&q=##echo%20makefile&type=cs)
In OpusMake, ## means, "really, really quiet". It causes OpusMake to suppress printing the commands even when invoked as make -n. Probably somebody, somewhere, had some familiarity with that feature, wrote their makefiles to use it, somebody else saw it and copied it, and since it doesn't break other make variants (at least, not GNU make), it just stuck around.
Looking at the code, it seems that it just strips all the leading # (or +/-), but I'm not 100% sure (that is, you can put there as many # as you wish) - look at job.c in make source code.
while (*p != '\0')
{
if (*p == '#')
flags |= COMMANDS_SILENT;
else if (*p == '+')
flags |= COMMANDS_RECURSE;
else if (*p == '-')
child->noerror = 1;
else if (!isblank ((unsigned char)*p))
break;
++p;
}
Related
Consider the following GNU makefile snippet:
# this code is not under my control
A = aaa
B = bbb
# this code is under my control
A += $B
B = ccc
# Prints "aaa ccc". I would rather "aaa bbb".
$(info $A)
The issue is that since A is initially created as a deferred variable, the value of the RHS of the += is taken as a deferred value. Is there something like A += $(immediate B) that I can use?
The following somewhat gets there:
TMP := $B
A += $TMP
but is not an option since this will be in an include file, and gets used several times (in the context of a non-recursive make system). So TMP would get clobbered each time.
UPDATE
A little more justification. This is a non-recursive build, and variable d holds the name of the directory being processed. An include file is invoked for each subdirectory, setting up some variables. For example:
$d.LIBS += $($d.target_dir)/lib.a
LD_FLAGS += $($d.LIBS)
The problem here is that d is ephemeral, with its value changing as soon as the next directory is processed. LD_FLAGS and, for that matter, $d.LIBS may need to remain as deferred variables but d needs to be evaluated immediately here.
If you don't mind A becoming immediate, then you can just use this:
A := $A $B
If you want A to continue to be deferred, but to expand $B immediately, you can use eval because eval expands its argument:
$(eval A += $B)
This will expand $B first, so you get A += bbb, then evaluate that.
If you want some aspects of the value expanded and others not, just escape the not-to-be-expanded content:
$(eval LD_FLAGS += $$($d.LIBS))
I should say, though, that usually the things you're trying to do are handled more by constructed variable names based on the target name, so something like:
LD_FLAGS += $($#_LDFLAGS)
or whatever.
I have a code file that has some #ifdefs I would like removed in the header file after building a library. My first thought was to do this as a perl script that XCode can run. While I can certainly open the header file and read all content of it into a string in perl, I'm curious as to the best way to do the following
Find any occurrence of #ifdef EXAMPLE
Remove it and anything in between the following #endif
So the example is:
int i;
NSString *someString;
#ifdef EXAMPLE
NSString *exampleString;
#endif
bool done;
and the output would be:
int i;
NSString *someString;
bool done;
Options I'm considering:
finding index of every #ifdef EXAMPLE and removing it via substring with the next found #endif
Write a regex that can somehow remove these occurences.
Considering I haven't written Perl before (Objective-C is my primary language) I was curious if any XCode or Perl developers had any suggestions on what the best approach would be
I'm not sure why you want to strip out ifdefs, and you can probably use a C pre-processor to do this, but here's how you'd do it in Perl because it means I get to play with the flip-flop operator.
First thing is to craft a sufficient regex to match the ifdefs. IIRC they can be indented and there can be indentation between the # and the word.
#ifdef
# ifdef
#ifdef
Not sure if that last one is valid, but I'm going with it anyway.
my $ifdef_re = qr{^\s*#\s*ifdef\b};
my $endif_re = qr{^\s*#\s*endif\b};
If it was just removing text between #ifdef and #endif, Perl has the little used flip flop scalar .. operator.
#!/usr/bin/env perl
use strict;
use warnings;
my $ifdef_re = qr{^\s*#\s*ifdef\b};
my $endif_re = qr{^\s*#\s*endif\b};
while(<DATA>) {
my $in_ifdef = /$ifdef_re/ .. /$endif_re/;
print if !$in_ifdef;
}
__DATA__
int i;
NSString *someString;
#ifdef EXAMPLE
NSString *exampleString;
#endif
bool done;
But since we need to worry about nested ifdefs, its insufficient. A depth counter takes care of that.
#!/usr/bin/env perl
use strict;
use warnings;
my $ifdef_re = qr{^\s*#\s*ifdef\b};
my $endif_re = qr{^\s*#\s*endif\b};
my $ifdef_count = 0;
while(<DATA>) {
$ifdef_count++ if /$ifdef_re/;
print if $ifdef_count <= 0;
$ifdef_count-- if /$endif_re/;
}
__DATA__
int i;
NSString *someString;
#ifdef EXAMPLE
NSString *exampleString;
# ifdef FOO
this should not appear
# endif
nor should this
#endif
bool done;
I love regexes, but for this problem I wouldn't use a regex, I'd just read line by line, keeping track of whether I was inside a ifdef:
my $nesting = 0;
while (<STDIN>)
{
$nesting += 1 if /^#ifdef/;
print $_ unless $nesting;
$nesting -= 1 if /^#endif/;
}
If you really want to use a regex, and have read the whole file into the variable $source, I think this will work, if you don't need to worry about nesting:
$source =~ s/^#ifdef.*?^#endif.*?$//gms;
The ^ characters anchor those parts of the expression to the beginning of a line. The $ makes the last part of the match only happen at the end of a line.
The .*? behaves almost like .*, which matches zero or more characters, except that it does minimal matching. So instead of matching all the way to the last #endif, it matches to the first one.
The /gms at the end makes it:
Substitute every occurrence, not just one (that's the g)
Make ^ and $ match at line boundaries, not just string boundaries (the m)
Make . match newlines (the s)
You might want to follow every #ifdef and #endif with \s, to only match if there is whitespace following that string.
I'd just do this with unifdef. XCode installs this by default:
-U will remove #ifdef and matching #else/#endif as if <constant> is undefined.
-D will remove #ifdef and matching #else/#endif as if <constant> is defined.
Here's an example:
$ cat test.h
#ifdef TEST
#ifdef DEBUG
# define AWESOME_DEBUG_LEVEL 1
#else
# define AWESOME_DEBUG_LEVEL 0
#endif
#endif
$ unifdef -U DEBUG test.h
#ifdef TEST
# define AWESOME_DEBUG_LEVEL 0
#endif
$ unifdef -U DEBUG -D TEST test.h
# define AWESOME_DEBUG_LEVEL 0
I am currently writting a Ruby parser using Ruby, and more precisely Parslet, since I think it is far more easier to use than Treetop or Citrus. I create my rules using the official specifications, but there are some statements I can not write, since they "exclude" some syntax, and I do not know how to do that... Well, here is an example for you to understand...
Here is a basic rule :
foo::=
any-character+ BUT NOT (foo* escape_character barbar*)
# Knowing that (foo* escape_character barbar*) is included in any-character
How could I translate that using Parslet ? Maybe the absent?/present? stuff ?
Thank you very much, hope someone has an idea....
Have a nice day!
EDIT:
I tried what you said, so here's my translation into Ruby language using parslet:
rule(:line_comment){(source_character.repeat >> line_terminator >> source_character.repeat).absent? >> source_character.repeat(1)}
However, it does not seem to work (the sequence in parens). I did some tests, and came to the conclusion that what's written in my parens is wrong.
Here is a very easier example, let's consider these rules:
# Parslet rules
rule(:source_character) {any}
rule(:line_terminator){ str("\n") >> str("\r").maybe }
rule(:not){source_character.repeat >> line_terminator }
# Which looks like what I try to "detect" up there
I these these rules with this code:
# Code to test :
code = "test
"
But I get that:
Failed to match sequence (SOURCE_CHARACTER{0, } LINE_TERMINATOR) at
line 2 char 1. - Failed to match sequence (SOURCE_CHARACTER{0, }
LINE_TERMINATOR) at line 2 char 1.- Failed to match sequence (' '
' '?) at line 2 char 1.
`- Premature end of input at line 2 char 1. nil
If this sequence doesn't work, my 'complete' rule up there won't ever work... If anyone has an idea, it would be great.
Thank you !
You can do something like this:
rule(:word) { match['^")(\\s'].repeat(1) } # normal word
rule(:op) { str('AND') | str('OR') | str('NOT') }
rule(:keyword) { str('all:') | str('any:') }
rule(:searchterm) { keyword.absent? >> op.absent? >> word }
In this case, the absent? does a lookahead to make sure the next token is not a keyword; if not, then it checks to make sure it's not an operator; if not, finally see if it's a valid word.
An equivalent rule would be:
rule(:searchterm) { (keyword | op).absent? >> word }
Parslet matching is greedy by nature. This means that when you repeat something like
foo.repeat
parslet will match foo until it fails. If foo is
rule(:foo) { any }
you will be on the path to fail, since any.repeat always matches the entire rest of the document!
What you're looking for is something like the string matcher in examples/string_parser.rb (parslet source tree):
rule :string do
str('"') >>
(
(str('\\') >> any) |
(str('"').absent? >> any)
).repeat.as(:string) >>
str('"')
end
What this says is: 'match ", then match either a backslash followed by any character at all, or match any other character, as long as it is not the terminating ".'
So .absent? is really a way to exclude things from a match that follows:
str('foo').absent? >> (str('foo') | str('bar'))
will only match 'bar'. If you understand that, I assume you will be able to resolve your difficulties. Although those will not be the last on your way to a Ruby parser...
I have the following problem: in an interactive script, when asking for input, I want to display a suggestion and make it editable. It is a similar functionality to "arrow up and edit the last command" in a command prompt, except without the "arrow up". I tried several different things but no success so far.
These are the things I tried:
1) Get input from editor, like so:
echo "$SUGGESTION\c"
INPUT=`ed -` # problem with this approach is that 'ed' starts in command mode
# by default, and I would need input mode
2) Use read -e
echo "$SUGGESTION\c"
read -e INPUT # doesn't work as advertised
After extensive Googling I am convinced that the 2) should work, but it doesn't. First of all, I cannot delete the $SUGGESTION without typing some input first; after some characters are typed, backspace deletes the whole line, not just one character.
So my question is: how to make "read -e" work or is there another approach to solve this? Your help is very much appreciated!
It does work as advertised, but you need an extra parameter to do what you want:
read -e -i "$SUGGESTION" INPUT
Unfortunately, that's only available in Bash 4.
If you have a C compiler and readline available, here's a quick hack that you could use. Save the following to myread.c (or whatever) and compile it (you'll need to link with readline). For GCC, that would be: gcc -o myread myread.c -lreadline.
#include <stdio.h>
#include <readline/readline.h>
int main(int argc, char **argv)
{
if (argc != 2)
return 1;
// stuff the input buffer with the default value
char *def = argv[1];
while (*def) {
rl_stuff_char(*def);
def++;
}
// let the user edit
char *input = readline(0);
if (!input)
return 1;
// write out the result to standard error
fprintf(stderr, "%s", input);
return 0;
}
You can use it like this:
myread "$SUGGESTION" 2> some_temp_file
if [ $? -eq 0 ] ; then
# some_temp_file contains the edited value
fi
Lots of room for improvement, but I guess it's a start.
I've got a ruby bin with some arguments, namely -s, -c and -r (short for scrape, create and run). Now I'd like to set some defaults to scrape and create ('.' in both cases), but if I use :default in trollop, I can't check wherever that argument is set or not.
project --scrape
should be equivalent to
project --scrape .
how to achieve that?
And while at it, how do I make
project target
to be equivalent with
project --run target
?
You can modify ARGV before Trollop processes it. Your best bet would probably be to scan the input arguments, apply some basic transformations, and then run Trollop.
For example:
args = ARGV.split
idx = args.index '--scrape'
if idx != nil
if idx < args.length
if args[idx + 1][0..1] == '--'
args=args[0..idx] + ['.'] + args[idx+1..-1]
end
else
if args[idx + 1][0..1] == '--'
args << '.'
end
end
end
This snippet should check for --scrape with no parameter following it and add in a '.' in that case. You can do something similar to check for the omitted --run parameter. When you are done making your modifications, use args.join(' ') to put the arguments back together into a string. Assign this new string to ARGV, and then set Trollop loose.