I am working on the Makefile someone else in my lab wrote, and I see a command called true in some rules:
other_rule_A: YYY force_it
....
other_rule_B: XXX force_it
....
force_it:
true
What does true mean here? Is it a command run by the subshell? What would happen if I replace it by false? Is true a binary invoked by the shell?
For reference, I found this other question in Stackoverflow: Why do makefiles sometimes have 'true ' as part of the build script?, but I don't think command is related to the command they cover in that thread.
true is a command (usually the executable /bin/true, but often also a shell builtin) that does nothing and returns success. If you were to replace it with false, the force_it: rule would always fail and thus stop the make process at that point.
The difference between having this true action and having a force_it: rule with no action is that with no action, there might be some other force_it: rule with actions added elsewhere in the makefile. With a true action, you know that there can be no other action -- if another action was added, you'd get a make error.
Related
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).
I'm creating a user via Chef. His properties are stored in data bag:
{
"id": "developer",
"home": "/home/developer",
"shell": "/bin/zsh",
"password": "s3cr3t"
}
The recipe is:
developer = data_bag_item('users', 'developer')
user developer['id'] do
action :create
supports :manage_home => true
home developer['home']
comment developer['comment']
shell developer['shell']
password developer['password']
end
The problem is that if zsh is not installed on node, I cannot login as developer. So, I want to conditionally apply argument for user resource, like:
user developer['id'] do
action :create
supports :manage_home => true
home developer['home']
comment developer['comment']
if installed?(developer['shell'])
shell developer['shell']
end
password developer['password']
end
How can I achieve this?
To complement #mudasobwa's answer the proper way to do it in chef and avoid missing the shell if it's installed by another recipe or a package resource in the same recipe you have to use lazy attribute evaluation.
Long version for thoose interested on the how and why:
This is a side effect on how chef works, there's a first time compiling the resources to build a collection, at this phase any ruby code in a recipe (outside of a ruby_block resource) if evaluated. Once that is done the resources collection is converged (the desired state is compared to the actual state and relevant actions are done).
The following recipe would do:
package "zsh" do
action :install
end
user "myuser" do
action :create
shell lazy { File.exists? "/bin/zsh" ? "/bin/zsh" : "/bin/bash" }
end
What hapens here is that the evaluation of the shell attribute value is delayed to the converge phase, we have to use a if-then-else construction (here with a ternary operator as I find it more readable) to fallback to a shell we're sure will be present (I used /bin/bash, but a failsafe value would be /bin/sh) or the shell attribute will be nil, which is not allowed.
With this delayed evaluation the test on the presence of "/bin/zsh" is done after the package has been installed and the file should be present. In case there was a problem within the package, the user resource will still create the user but with "/bin/bash"
The easiest way to achieve what you want is to check for the shell existence explicitly:
shell developer['shell'] if File.exist? developer['shell']
I recently asked a question asking about how to test whether a directory/file exists in Ruby and I was redirected to this great post that has a lot of wonderful answers. Directory/File test existence answer.
The thing is though, I don't understand why my method doesn't work for testing to see if a directory exists. Here is what I have:
task :make_tmp_writable do
on roles(:slave) do
if test ?d "#{current_path}/tmp"
execute "chmod g+w #{File.join(current_path,'tmp')}"
else
execute "mkdir #{current_path}/tmp"
execute "chmod g+w #{File.join(current_path,'tmp')}"
end
end
end
This test always passes true every time and I don't understand why. On my linux system, the current_path/tmp file is not physically present on the system at all, yet this test always returns true. Why is it always returning true? This also seems to be the case with methods such as
FileTest.exist?(...)
Directory.exist?(....)
Can someone please explain to me why this keeps happening?
-----------EDIT----------------
I've tried experimenting with this method and the Directory/File/Kernel methods do work but not in the way that I'd like.
For example if you run these methods with keywords such as /var , /tmp, /bin, /sbin, etc. you will receive a true. My current_path is equal to /var/local/app/current. If I were to append a /var at the end, it will be false, but tmp returns true regardless. This indicates that there must be a duplicate tmp in my file system but it simply isn't there. Are there any other leads I can follow?
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.
i would like to show you my use case and then discuss possible solutions:
Problem A:
i have 2 recipes, "a" and "b".. "a" installs some program on my file system (say at "/usr/local/bin/stuff.sh" and recipe "b" needs to run this and do something with the output.
so recipe "a" looks something like:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh"
(the script just echo(es) "stuff" to stdout)
and recipe "b" looks something like:
include_recipe "a"
var=`/usr/local/bin/stuff.sh`
(note the backquotes, var should contain stuff)
and now i need to do something with it, for instance create a user with this username. so at script "b" i add
user "#{node[:var]}"
As it happens, this doesn't work.. apparently chef runs everything that is not a resource and only then runs the resources so as soon as i run the script chef complains that it cannot compile because it first tries to run the "var=..." line at recipe "b" and fails because the "execute ..." at recipe a did not run yet and so the "stuff.sh" script does not exist yet.
Needless to say, this is extremely annoying as it breaks the "Chef runs everything in order from top to bottom" that i was promised when i started using it.
However, i am not very picky so i started looking for alternative solutions to this problem, so:
Problem B: i've run across the idea of "ruby_block". apparently, this is a resource so it will be evaluated along with the other resources. I said ok, then i'd like to create the script, get the output in a "ruby_block" and then pass it to "user". so recipe "b" now looks something like:
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`
end
end
user "#{node[:var]}"
However, as it turns out the variable (var) was not passed from "ruby_block" to "user" and it remains empty. No matter what juggling i've tried to do with it i failed (or maybe i just didn't find the correct juggling method)
To the chef/ruby masters around: How do i solve Problem A? How do i solve Problem B?
You have already solved problem A with the Ruby block.
Now you have to solve problem B with a similar approach:
ruby_block "create user" do
block do
user = Chef::Resource::User.new(node[:var], run_context)
user.shell '/bin/bash' # Set parameters using this syntax
user.run_action :create
user.run_action :manage # Run multiple actions (if needed) by declaring them sequentially
end
end
You could also solve problem A by creating the file during the compile phase:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh" do
action :nothing
end.run_action(:run)
If following this course of action, make sure that:
/usr/local/bin exist during Chef's compile phase;
Either:
stuff.sh is executable; OR
Execute it through a shell (e.g.: var=`sh /usr/local/bin/stuff.sh`
The modern way to do this is to use a custom resource:
in cookbooks/create_script/resources/create_script.rb
provides :create_script
unified_mode true
property :script_name, :name_property: true
action :run do
execute "creating #{script_name}" do
command "echo 'echo stuff' > #{script_name}"
not_if { File.exist?(script_name) }
end
end
Then in recipe code:
create_script "/usr/local/bin/stuff.sh"
For the second case as written I'd avoid the use of a node variable entirely:
script_location = "/usr/local/bin/stuff.sh"
create_script script_location
# note: the user resources takes a username not a file path so the example is a bit
# strange, but that is the way the question was asked.
user script_location
If you need to move it into an attribute and call it from different recipes then there's no need for ruby_blocks or lazy:
some cookbook's attributes/default.rb file (or a policyfile, etc):
default['script_location'] = "/usr/local/bin/stuff.sh"
in recipe code or other custom resources:
create_script node['script_location']
user node['script_location']
There's no need to lazy things or use ruby_block using this approach.
There are actually a few ways to solve the issue that you're having.
The first way is to avoid the scope issues you're having in the passed blocks and do something like ths.
include_recipe "a"
this = self
ruby_block "a_block" do
block do
this.user `/usr/local/bin/stuff.sh`
end
end
Assuming that you plan on only using this once, that would work great. But if you're legitimately needing to store a variable on the node for other uses you can rely on the lazy call inside ruby to do a little work around of the issue.
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`.strip
end
end
user do
username lazy { "#{node[:var]}" }
end
You'll quickly notice with Chef that it has an override for all default assumptions for cases just like this.