Get autocompletion list in bash variable - bash

I'm working with a big software project with many build targets. When typing make <tab> <tab> it shows over 1000 possible make targets.
What I want is a bash script that filters those targets by certain rules. Therefore I would like to have this list of make targets in a bash variable.
make_targets=$(???)
[do something with make_targets]
make $make_targets
It would be best if I wouldn't have to change anything with my project.
How can I get such a List?

#yuyichao created a function to get autocomplete output:
comp() {
COMP_LINE="$*"
COMP_WORDS=("$#")
COMP_CWORD=${#COMP_WORDS[#]}
((COMP_CWORD--))
COMP_POINT=${#COMP_LINE}
COMP_WORDBREAKS='"'"'><=;|&(:"
# Don't really thing any real autocompletion script will rely on
# the following 2 vars, but on principle they could ~~~ LOL.
COMP_TYPE=9
COMP_KEY=9
_command_offset 0
echo ${COMPREPLY[#]}
}
Just run comp make '' to get the results, and you can manipulate that. Example:
$ comp make ''
test foo clean

You would need to overwrite / modify the completion function for make. On Ubuntu it is located at:
/usr/share/bash-completion/completions/make
(Other distributions may store the file at /etc/bash_completion.d/make)
If you don't want to change the completion behavior for the whole system, you might write a small wrapper script like build-project, which calls make. Then write a completion function for that mapper which is derived from make's one.

Related

Can reuse makefile multiple times within single execution?

Suppose I have:
# ./Makefile
CLUSTER=dev
include Makefile.cluster.mk
CLUSTER=local
include Makefile.cluster.mk
And in:
# ./Makefile.cluster.mk
${CLUSTER}.cmd:
cmd ${CLUSTER}
So now I can call:
make dev.cmd
make local.cmd
Great! Except the variable is evaluated too late. Running:
$ make local.cmd # cmd local
$ make dev.cmd # Also cmd local !
Make sense: according to: https://www.gnu.org/software/make/manual/html_node/Reading-Makefiles.html
rule steps are deferred evaluation (vs. immediate/on file load).
immediate : immediate ; deferred
deferred
Is there a better/other way to compose a set of make commands without maintaining multiple copies of the same file?
There are lots of ways to do it, even beyond the options above; you could use static pattern rules:
CLUSTERS := dev local
$(CLUSTERS:%=%.cmd) : %.cmd :
cmd $*
If you really want to have stuff in a separate makefile you can use target-specific variables; change your Makefile.cluster.mk to do this:
# ./Makefile.cluster.mk
${CLUSTER}.cmd: CLUSTER := $(CLUSTER)
${CLUSTER}.cmd:
cmd ${CLUSTER}
Is there a better/other way to compose a set of make commands without maintaining multiple copies of the same file?
Often it's pattern rules. In the case of your particular example, you might do
Makefile
%.cmd:
cmd '$*'
However, that particular version will enable any make foo.cmd, which might not be what you want.
Sometimes it's to make better use of the tools available to you. For example,
Makefile.cluster.mk
${CLUSTER}.cmd:
arg='$#'; cmd "$${arg%.cmd}"
That extracts the wanted cluster name from the name of the target.
Occasionally it is $(eval).
(See the manual for an example.)
And from time to time, it's "don't do that." For example,
Makefile
CLUSTERS = dev local
CMDS = $(patsubst %,%.cmd,$(CLUSTERS))
$(CMDS):
arg='$#'; cmd "$${arg%.cmd}"
That defines only dev.cmd and local.cmd targets, and avoids duplicating the recipe.

Where should I start to debug when Make throws a particular error

My knowledge of Make is small. I have been told that everything you put after make (that does not contain "-") is a target.
Well a building process I have is failing.
First there is a line
make path/to/configuration_file
configuration_file is not a target. It is a autogenerated configuration file buried inside the directory structure ("path/to") that is of the form
#
# Boot Configuration
#
#
# DRAM Component
#
CONFIG_DRAM_TYPE_LPDDR4=y
# CONFIG_DRAM_TYPE_DDR4 is not set
CONFIG_DDR_SIZE=0x80000000
#
# Boot Device
#
# CONFIG_ENABLE_EMMC_BOOT is not set
# CONFIG_ENABLE_NAND_BOOT is not set
CONFIG_ENABLE_SPINAND_BOOT=y
# CONFIG_ENABLE_SPINOR_BOOT is not set
CONFIG_EMMC_ACCESS_8BIT=y
# CONFIG_EMMC_ACCESS_4BIT is not set
# CONFIG_EMMC_ACCESS_1BIT is not set
so I cannot understand how this is a target. For reference, when I run make there is a Makefile but this Makefile does not reference this file.
Still this line is going well.
The path where it fails says
make diags
and I have verified there is no "diags" target.
I will print here the error file that can give us more info of what is happening
GEN cortex_a/output/Makefile
Init diag test "orc_scheduler" ...
remoteconfig: Failed to generate configure in cortex_a/soc/visio/tests/orc_scheduler!
Makefile:11 recipe for target 'orc_scheduler-init' failed
make[10]: *** [orc_scheduler-init] Error 25
At least what I would like to know is how to interpret this error message. I don't know what the "11" or the "10" or the "25" refers to.
make is fundamentally a tool for automatically running commands in the right order so you don't have to type them in yourself. So all the commands make runs are commands that you could just type into your shell prompt. And all the errors that those commands generate are the same ones that you would see if you typed the command yourself. So, looking at make to try to understand those errors is looking in the wrong place: you have to look at the documentation for whatever command was invoked.
A "target" is just a file that make knows how to build. The fact that when you typed make <somefile> is didn't give you an error that it doesn't know how to build <somefile>, means that <somefile> is a target as far as your makefiles are concerned.
The error message Makefile:11: simply refers to the filename Makefile, line 11, which is where the command that make ran, that failed, can be found. But this likely won't help you solve the problem of why the command failed (unless the problem is you invoked it with the wrong arguments and you need to adjust the makefile to specify different arguments).
The command that failed generated the message:
remoteconfig: Failed to generate configure in cortex_a/soc/visio/tests/orc_scheduler!
I don't know what that means, but it's not related to make. You'll need to find out what this remoteconfig command is, what it does, and why it failed. It's unfortunate that it doesn't show any better error message as to why it failed to "generate configure", but again there's nothing make can do about that.
If you want to learn more about make you can look at the GNU make manual (note, GNU make is only one implementation of make; there are others and they are fundamentally the same but different in details).

Rake, how to create a task with multiple inputs and one output?

We have a rails app, and we use webpack which takes multiple javascript files and outputs one single javascript file. It takes a long time to run, and I'd like to create a rake task for this. But being new to rake I need some help.
I'd like to use rake's build system so that I can get automatic checking of the time stamps between the input and output .js files. So that if any of the input files are newer than the output file it will execute webpack. Otherwise if the none of the input files are newer than the output file, than the task does nothing.
In MSBuild, this is a cakewalk and lightning fast. But in Ruby I'm kind of lost.
I'm guessing it might consist of writing file tasks, and looping through and making the one output file depend on the inputs. Or should I use a rule, like this?
outputfile = "~/foo.js"
inputfiles = Dir["~/**/*.js"]
rule outputfile => inputfiles do
bin/webpack bla bla bla
end
You can use Rake::FileList to achieve this. Something like this:
file "foo.js" => Rake::FileList["**/*.js"] do
...
end
And, I'm not sure whether rake allows to use ~ in paths, I believe a full path is required. Or just use a "#{Dir.home}/foo.js" rule.
Then call it using:
rake ~/foo.js
And when you have multiple outputs:
task :build => Rake::FileList["config1.xml", "config2.xml"] do
# all that stuff is run only when the FileList above is changed
touch 'foo1.js'
touch 'foo2.js'
sh "compile foo3.js"
sh "do-anything-else foo4.js"
end
Run it using:
rake build

How do you create a Bash function that accepts files of a specific type as arguments?

So far, I know that you have to create a function in order to pass arguments.
However, how do you denote the type of the argument?
For instance, if you want to compile a Java class file and then run the resulting Java file (without having to type the file name twice to distinguish between the extensions each time), how do you let the function know that the names belong to files of different types?
Let's say this is our function:
compileAndRun()
{
javac $1
java $2 # basically, we want to make this take the same argument
# (because the names of the *.class and *.java files are the same)
}
So, instead of typing:
compileAndRun test.class test.java
We wanna just type this:
compileAndRun test
Any help along with any extraneous information you wanna throw in would be much appreciated.
Just use $1 twice. It is safer to connect the two commands with &&, so java is not run if the compilation is not successful.
function compile_n_run () {
javac "$1".java && java "$1".class
}
Arguments to bash functions don't really have types; they are just strings, and it's up to you to use them appropriately. In this case, it's fairly simply to write a function which takes a Java source file, compiles it, and runs the resulting output.
compile_n_run () {
source=$1
expected_output="${source%.java}.class"
javac "$source" && java "$expected_output"
}
$ compile_n_run test.java
I chose to require the full Java source name because it's a little friendlier with auto-completion; you don't have to remove the .java from the command-line, rather you let the function do that for you. (And otherwise, this answer would be identical to choroba's).

Why does make lose variable settings when called recursively?

I barely know enough makefile stuff to hack away at other people's makefiles, and sometimes not even that much. My current employer has a fairly sophisticated make environment, and I added a couple targets to the makefile, mostly to self-document all the switches I needed. (There might be a better way to do this...if there is, please let me know!!)
Anyway, the first target works as expected. The second target is identical to the first, except that the BENCH assignment has a "2" at the end. Otherwise, its the same exact call. At some point during execution, the BENCH variable gets used to create a directory. The first target creates the directory as expected. The second target creates the directory using the default BENCH name instead of the one passed to it. Somehow it lost the variable setting at the command line, even though almost identical syntax worked for the first target.
Why does it lose my variable settings when it calls itself? Is there some make subtlety that I'm missing? Is this even a valid thing to do?
comp_gate_oct04_ff:
make clean all BENCH=GATE_OCT04_FF SIMULATOR=NC SIM_64BIT=1 CORNER=FF NETLIST=oct04 | tee all_gate_oct04_ff.log
#tail all_gate_oct04_ff.log | mail -s "Compile for GATE_OCT04_FF is complete" $(SMS_ADDR) $(USER)
comp_gate_oct04_ff2:
make clean all BENCH=GATE_OCT04_FF2 SIMULATOR=NC SIM_64BIT=1 CORNER=FF NETLIST=oct04 | tee all_gate_oct04_ff2.log
#tail all_gate_oct04_ff2.log | mail -s "Compile for GATE_OCT04_FF2 is complete" $(SMS_ADDR) $(USER)
I can't see why you're getting this error, and I can't reproduce it. Try something simpler:
comp_gate_oct04_ff:
make foo BENCH=GATE_OCT04_FF
comp_gate_oct04_ff2:
make foo BENCH=GATE_OCT04_FF2
foo:
#echo BENCH is $(BENCH)
Tell us how this behaves and we'll go from there.
EDIT:
All right, we've cut the problem in half. Now try this:
comp_gate_oct04_ff:
make clean
make all BENCH=GATE_OCT04_FF SIMULATOR=NC SIM_64BIT=1 CORNER=FF NETLIST=oct04
comp_gate_oct04_ff2:
make clean
make all BENCH=GATE_OCT04_FF2 SIMULATOR=NC SIM_64BIT=1 CORNER=FF NETLIST=oct04

Resources