I have a go generate directive which looks like this:
//go:generate myprog -someName thisname -data 'Request: Typ "." callMe, Rsp: MyTyp "." close'
The issue is that the program receives only value of -someName flag ("thisname"). I assume the -data flag is discarded for some reasons. Any idea why? It's working if I execute the program directly from command line so I guess it's a go specific issue.
From the design document of go generate https://docs.google.com/document/d/1V03LUfjSADDooDMhe-_K59EgpTEm3V8uvQRuNMAEnjg/edit:
The arguments are space-separated tokens (or double-quoted strings) passed to the generator as individual arguments when it is run.
So if you want to pass an argument containing space you will have to double quote them. You used single quotes which works in your shell but not with go generate
Related
I have a list/array like so:
['path/to/folder/a', 'path/to/folder/b']
This is an example, the array can be of any length. But for each item in the array I'd like to set up the following as a single command:
$ someTool <command> --flag <item-1> --flag <item-2> ... --flag <item-N>
At the moment I am currently doing a loop over the array but I am just wondering if doing them individually has a different behaviour to doing them all at once (which the tool specifies I should do).
for i in "${array[#]}"; do
someTool command --flag $i
done
Whether passing all flag arguments to a single invocation of the tool does the same thing as passing them one-at-a-time to separate invocations depends entirely on the tool and what it does. Without more information, it's impossible to say for sure, but if the instructions recommend passing them all at once, I'd go with that.
The simplest way to do this in bash is generally to create a second array with the flags and arguments as they need to be passed to the tool:
flagsArray=()
for i in "${array[#]}"; do
flagsArray+=(--flag "$i")
done
someTool command "${flagsArray[#]}"
Note: all of the above syntax -- all the quotes, braces, brackets, parentheses, etc -- matter to making this run properly and robustly. Don't leave anything out unless you know why it's there, and that leaving it out won't cause trouble.
BTW, if the option (--flag) doesn't have to be passed as a separate argument (i.e. if the tool allows --flag=path/to/folder/a instead of --flag path/to/folder/a), then you can use a substitution to add the --flag= bit to each element of the array in a single step:
someTool command "${array[#]/#/--flag=}"
Explanation: the /# means "replace at the beginning (of each element)", then the empty string for the thing to replace, / to delimit that from the replacement string, and --flag= as the replacement (/addition) string.
I'm trying to get a simple build bash script to work and can't get the date as an argument. What am I missing here? Thanks.
#!/bin/bash
buildDate=$(date)
go build -ldflags='-X main.buildTimestamp=$buildDate' main.go
Output: Build: $buildDate
To quote the documentation on a POSIX-compatible shell:
2.2.2 Single-Quotes
Enclosing characters in single-quotes ( '' ) shall preserve the literal value of each character within the single-quotes. A single-quote cannot occur within single-quotes.
So, in your particular case, bash breaks the go build … command into words and performs string substitution before actually executing the command.
As per string substitution rules, the text enclosed with single quotes is taken literally, so after the expansion the shell looks up the go command and executes it pasing it the tree resulting arguments, which are, in order: build, -ldflags=-X main.buildTimestamp=$buildDate and main.go.
Since the Go toolchain does not invoke shell anywhere in its course of work, the literal text $buildDate gets passed unmodified to the compiler.
If the format of your build date does not include whitespace, the easiest fix is to just replace the single quotes with double quotes.
If you need to embed spaces in there, it gets a little bit more tricky but not too much — to cite the output of go build help:
The -asmflags, -gccgoflags, -gcflags, and -ldflags flags
accept a space-separated list of arguments to pass
to an underlying tool during the build.
To embed spaces in an element in the list, surround
it with either single or double quotes.
<…>
In other words, the command-line argument parser considers pairs of "s and 's to implement argument grouping, and in your case you could do
go build -ldflags="-X main.buildTimestamp='$buildDate'" main.go
so that the shell strips out the outer double quotes and substitutes $buildDate for its value — as per the rules of string substitution for the case of double quotes.
Hence, if your build date format includes spaces, like in, say, Tue, 12 May 2020 20:53:16 +0300, the go command would receive the following three arguments, in order: build, -ldflags=-X main.buildTimestamp='Tue, 12 May 2020 20:53:16 +0300' and main.go, and would take care of those single quotes by itself.
I have a text file called OPTIONS.txt storing all flags of Makefile:
arg1=foo arg2="-foo -bar"
I want to pass all flags in this file to make. However,
make `cat OPTIONS.txt`
fails with make: invalid option -- 'a'. It seems that shell interprets it as:
make arg1=foo arg2="-foo -bar"
^argv[1] ^argv[2] ^argv[3]
Is there any way to make it interpreted as:
make arg1=foo arg2="-foo -bar"
^argv[1] ^--------argv[2]
Since you control the options file, store the options one per line:
arg1=foo
arg2="-foo -bar"
Then in the shell, you'll read the file into an array, one element per line:
readarray -t opts < OPTIONS.txt
Now you can invoke make and keep the options whole:
make "${opts[#]}"
If you want the shell to interpret quotes after backtick expansion you need to use eval, like this:
eval make `cat OPTIONS.txt`
however just be aware that this evaluates everything, so if you have quoted content outside of the backticks you'll get the same issue:
eval make `cat OPTIONS.txt` arg4="one two"
will give an error. You'd have to double-quote the arg4, something like this:
eval make `cat OPTIONS.txt` arg4='"one two"'
In general it's tricky to do stuff like this from the command line, outside of scripts.
ETA
The real problem here is that we don't have a set of requirements. Why do you want to put these into a file, and what kind of things are you adding; are they only makefile variable assignments, or are there other make options here as well such as -k or similar?
IF the OP controls (can change) the format of the file AND the file contains content only used by make AND the OP doesn't care about the variables being command line assignments vs. regular assignments AND there are only variable assignments and not other options, then they can just (a) put each variable assignment on its own line, (b) remove all quotes, and (c) use include OPTIONS.txt from inside the makefile to "import" them.
I'm using jamplus to build a vendor's cross-platform project. On osx, the C tool's command line (fed via clang to ld) is too long.
Response files are the classic answer to command lines that are too long: jamplus states in the manual that one can generate them on the fly.
The example in the manual looks like this:
actions response C++
{
$(C++) ##(-filelist #($(2)))
}
Almost there! If I specifically blow out the C.Link command, like this:
actions response C.Link
{
"$(C.LINK)" $(LINKFLAGS) -o $(<[1]:C) -Wl,-filelist,#($(2:TC)) $(NEEDLIBS:TC) $(LINKLIBS:TC))
}
in my jamfile, I get the command line I need that passes through to the linker, but the response file isn't newline terminated, so link fails (osx ld requires newline-separated entries).
Is there a way to expand a jamplus list joined with newlines? I've tried using the join expansion $(LIST:TCJ=\n) without luck. $(LIST:TCJ=#(\n)) doesn't work either. If I can do this, the generated file would hopefully be correct.
If not, what jamplus code can I use to override the link command for clang, and generate the contents on the fly from a list? I'm looking for the least invasive way of handling this - ideally, modifying/overriding the tool directly, instead of adding new indirect targets wherever a link is required - since it's our vendor's codebase, as little edit as possible is desired.
The syntax you are looking for is:
newLine = "
" ;
actions response C.Link
{
"$(C.LINK)" $(LINKFLAGS) -o $(<[1]:C) -Wl,-filelist,#($(2:TCJ=$(newLine))) $(NEEDLIBS:TC) $(LINKLIBS:TC))
}
To be clear (I'm not sure how StackOverflow will format the above), the newLine variable should be defined by typing:
newLine = "" ;
And then placing the carat between the two quotes and hitting enter. You can use this same technique for certain other characters, i.e.
tab = " " ;
Again, start with newLine = "" and then place carat between the quotes and hit tab. In the above it is actually 4 spaces which is wrong, but hopefully you get the idea. Another useful one to have is:
dollar = "$" ;
The last one is useful as $ is used to specify variables typically, so having a dollar variable is useful when you actually want to specify a dollar literal. For what it is worth, the Jambase I am using (the one that ships with the JamPlus I am using), has this:
SPACE = " " ;
TAB = " " ;
NEWLINE = "
" ;
Around line 28...
I gave up on trying to use escaped newlines and other language-specific characters within string joins. Maybe there's an awesome way to do that, that was too thorny to discover.
Use a multi-step shell command with multiple temp files.
For jamplus (and maybe other jam variants), the section of the actions response {} between the curly braces becomes an inline shell script. And the response file syntax #(<value>) returns a filename that can be assigned within the shell script, with the contents set to <value>.
Thus, code like:
actions response C.Link
{
_RESP1=#($(2:TCJ=#)#$(NEEDLIBS:TCJ=#)#$(LINKLIBS:TCJ=#))
_RESP2=#()
perl -pe "s/[#]/\n/g" < $_RESP1 > $_RESP2
"$(C.LINK)" $(LINKFLAGS) -o $(<[1]:C) -Wl,-filelist,$_RESP2
}
creates a pair of temp files, assigned to shell variable names _RESP1 and _RESP2. File at path _RESP1 is assigned the contents of the expanded sequence joined with a # character. Search and replace is done with a perl one liner into _RESP2. And link proceeds as planned, and jamplus cleans up the intermediate files.
I wasn't able to do this with characters like :;\n, but # worked as long as it had no adjacent whitespace. Not completely satisfied, but moving on.
I read the following command from the batch file to run Maven on Windows mvn.bat:
if not "_%M2_HOME:~-1%"=="_\" goto checkMBat
And
if "%#eval[2+2]" == "4" goto 4NTArgs
What does this batch script mean?
ADD 1
As I tried, it seems _%M2_HOME:~-1% returns the _ plus the last 1 letter of the environment variable "_%M2_HOME%. But what's the name of this syntax?
%VAR:~-1% gets the last character in the envvar. The first snippet verifies that the envvar M2_HOME doesn't end with \. Note: Maven's docs say,
Note: For Maven 2.0.9, also be sure that the M2_HOME doesn't have a '\' as last character.
This might be related. They probably want to prepend M2_HOME to subdir names and always include a dirsep. The variable substitution in "_%...%" is unaffected by the initial underscore. Experessing it that way just ensures that the underscore is at the beginning of the output. I can't say for certain, but it may have been expressed that way to avoid a backslashed quote, e.g. "\".
The second is not any CMD/batch that I'm familiar with. The comment (assuming this comes from mvn.bat) says "4NT shell", which I take to mean that this batch file could be run in the Take Command Console which probably has extensions to MS CMD features. For example, %#eval[...] probably does numeric evaluation in 4NT. This would effectively be a check to see if the script were running in a 4NT shell.
The first one takes the last character of %M2_HOME%, adds an underscore to the front, and checks to see if the resulting string is _\ - in short, it checks that the last character of %M2_HOME% is a backslash by using substrings.
The second one is how you determine if 4NT is installed on your computer; if it is, there will be a variable function called #eval.
I found the explanation to "_%M2_HOME:~-1%" below link. It's a variable substring operation.
http://ss64.com/nt/syntax-substring.html