Update submodule before including a sub-makefile - makefile

I have a makefile that includes another makefile. This happens very early in the makefile. Something like:
include $(SOME_SDK)/Makefile.defines
However SOME_SDK is a submodule.
I would like to run git submodule update --init --recursive before getting to the point where the include happens.
Ideally, this should not be part of any target.
How can I do that?

You cannot have this command as part of some target, because the include directive is fulfilled before any target is calculated.
But you can have any shell command (or script) executed at any place of the Makefile, even before the include.
dummy := $(shell git submodule update --init --recursive)
include $(SOME_SDK)/Makefile.defines

Related

What is meaning of Path-like target in Make?

I don't have idea that what is meaning of path-like target specification.
I would like to see execute commands in Makefile that generated by cmake to know that build process of clang.
I saw it with make -n command, it seems like executed other make command like following.
make -f utils/hmaptool/CMakeFiles/hmaptool.dir/build.make utils/hmaptool/CMakeFiles/hmaptool.dir/build
I have no idea what above make command do it.
In this command, target specification is path-like.(utils/hmaptool/CMakeFiles/hmaptool.dir/build)
What is meaning of this?
I know non-path-like target, for example make install or make clean and so on.
But I have no idea path-like target.
What is this??
The above command will use the makefile utils/hmaptool/CMakeFiles/hmaptool.dir/build.make, and will attempt to build the target utils/hmaptool/CMakeFiles/hmaptool.dir/build.
You will have to check the makefile to know what exactly build is. Probably a PHONY target to build everything in that folder.

How to only pass an argument value to a Makefile target?

I come from the NodeJS world so I consider the Makefile as the "scripts" part in an npm package.json, which may be wrong (or not ?) to do so.
So my idea is to automate repetitive actions when installing a new dependency by typing:
make install github.com/stretchr/testify
And find a way to get the github.com/stretchr/testify parameter without having to use the heavy parameter name-value declaration FOO=bar (=> make install DEP=github.com/stretchr/testify) generally suggested.
So, following this answer, I tried this:
install %:
go get $*
godep save ./...
git add Godeps vendor
git commit -m "godep: add $*"
but unsuccessfully: it runs go get without any param and git commit -m "godep: add".
Trials
1 - When I do that:
install %:
echo $*
I see my "github.com/stretchr/testify".
2 - When I do that:
install %:
go get ${*}
it loops twice and first run go get without any param, then runs go get github.com/stretchr/testify (as wished).
It looks like ${*} represents an "array" of params parsing characters groups after the target, the first one being the space between install and github.com/stretchr/testify and the second one being github.com/stretchr/testify.
You cannot use explicit targets and patterns in the same rule, so your rule % install: will not work.
You can do this with GNU make using the CMDGOALS variable, but it's very hackish and error-prone and I don't recommend it.
ARG := $(filter-out install,$(MAKECMDGOALS))
install:
go get $(ARG)
godep save ./...
git add Godeps vendor
git commit -m "godep: add $(ARG)"
As you can see you'll need to add handling for situations where there are no other arguments, or where there are more than one other argument, and of course you can't add any more targets without putting them in the filter-out list, etc.
Just... not a good way to do it IMO.
Why don't you do something like this instead:
install-%:
go get $*
godep save ./...
git add Godeps vendor
git commit -m "godep: add $*"
Then run:
make install-github.com/stretchr/testify
Make variables can probably do what you want:
host> cat Makefile
install:
go get $(P)
godep save ./...
git add Godeps vendor
git commit -m "godep: add $(P)"
host> make install P=github.com/stretchr/testify
But using make just for this is probably overkill. It is much more than a scripting language.

Stop GNU make from compiling by default

I just want to use GNU make to compress some files.
So I wrote the Makefile as follows:
lib.tar.lzma: $(shell find ~/lib -name "*")
rm -f lib.tar.lzma
tar -cavf lib.tar.lzma -C ~/ lib/
However, after I run make, it automatically compile the c++ source code in that directory.
How can I stop it from compiling them? I just want to compress them.
Update:
I got the following error:
<builtin>: recipe for target '/home/xxx/lib/app' failed
It seems a built-in recipe.
(We don't know your entire Makefile and your full file tree, so this is only a guess; I assume that you have shown us a fragment of your much bigger Makefile)
However, after I run make, it automatically compile the c++ source code in that directory.
This is probably happening because your $(shell find ~/lib -name "*") is expanded to something containing your object files. Since they are in your dependencies their source file is recompiled if it is newer. BTW you might want to use instead $(shell cd .. ; find lib -name "*") or if lib has no subdirectory even $(wildcard ../lib/*)
You probably don't need any dependency for that lib.tar.lzma target, so just have:
lib.tar.lzma:
rm -f lib.tar.lzma
tar -cavf lib.tar.lzma -C ~/ lib/
BTW, that -C ~/ perhaps should be -C $$HOME since make use /bin/sh to run commands, and that POSIX shell don't know about ~ ; perhaps a -C .. might be better ...
Perhaps you might write some shell script make-backup.sh to do a more clever tar and you would then code
lib.tar.lzma: make-backup.sh
./make-backup.sh $#
However, perhaps you do have dependencies (e.g. if you need to archive some generated files). Then you need to list them explicitly and wisely (you certainly don't want to depend on all the files; perhaps only the source ones). Also, you might not need to archive any object files *.o, if you have some (but YMMV).
I recommend using make --trace or remake -x to debug your Makefile.
BTW, having a Makefile only for a backup is useless; write a shell script instead.
I also strongly recommend using some version control system (like git) if you don't use any. Notice that git has an archive subcommand which might be a more clever backup.

Specifying path to "makefile" using "make" command

I would like to run a makefile from another place in the file system. How do I pass the location of the makefile to make?
if I stand in "/" and I would like to run a makefile that resists in "/dir/dir2/dir3/makefile", how do I add that to the make command?
I tried:
make --file=dir/dir2/dir3/makefile
but it did not worked.
All relative paths in the makefile will be relative to your current directory and not the directory of the makefile.
Assuming that you understand that and what you want to do is still going to work then you want the -f flag to specify the makefile to use. (Which is in the man page, the manual and the --help output.)
If, instead, what you mean is you want to cd to somewhere else and run make then perhaps you are looking for (cd /some/path && make)?
You can use the -C flag to specify the path to your makefile. This way you can execute it from a different directory.
The -f flag has a different use. With that flag you can execute a makefile with a name other than makefile.

Automatically define GOPATH on a per project basis

For every project I create, I have to do export GOPATH={path_to_project} every time I cd into the project dir. There has to be an easier way. Isn't there some way I can create a .bashrc or .bash_profile file for a given directory to define the GOPATH for that project?
For example, I have two go projects A and B. If I have a singular GOPATH that isn't redefined when I move between projects, then binaries for both projects will be stored in the same place. More importantly, binaries for third party libraries will be stored in the same place, so I have no way of maintaining multiple versions of the same library on a per project basis.
However, if I am able to define GOPATH on a per project basis, then all binaries and third party libraries are project dependent. This seems to be the common way of handling package management in most other language environments (ruby rbenv, python vertiualenv, etc.)
(Q2 2018:
Note that with the vgo (now "module") project, GOPATH might end up being deprecated in favor of a project-based workflow. That would avoid the manual project-based GOPATH I was proposing below, two years ago)
With Go 1.11 (August 2018), GOPATH can be optional, with modules.
You have a similar idea expressed in Manage multiple GOPATH dirs with ease, by Herbert Fischer (hgfischer), for a Linux/Unix environment (base on the question already mention in the comments above):
Just include the following snippet in your ~/.bashrc (or ~/.bash_profile) and reload your shell environment with source ~/.bashrc.
This snippet will create a shell function that will override the builtin command cd with a customized one that scans the entered directory, and every other above, for a file named .gopath.
cd () {
builtin cd "$#"
cdir=$PWD
while [ "$cdir" != "/" ]; do
if [ -e "$cdir/.gopath" ]; then
export GOPATH=$cdir
break
fi
cdir=$(dirname "$cdir")
done
}
Now you just need to create a .gopath file in every directory you want as your GOPATH and every time you enter this directory, the redefined cd function will set the GOPATH of your current environment to this directory.
Update 2017: if you don't want to modify your environment, you can still use one GOPATH per project, by opening the src folder of that project in Visual Studio Code (vscode, which is a multi-platform IDE), combined with the extension "Go for Visual Studio Code".
In that IDE, you can:
keep your global unique GOPATH in a setting called go.toolsGopath.
infer your current GOPATH with a setting called go.inferGopath
That way, VSCode will install a collection of tools in your global GOPATH (for you to use outside VSCode as well).
See "Go tools that the Go extension depends on": godep, golint, guru, godoc, ...
And yet, your GOPATH for your project will be the parent folder of src:
That works when you compile/install your project from the IDE.
If you want to do it from the command line, the original answer above would still apply.
You can use a tool like autoenv to set up a script that is automatically executed when you cd into a particular directory.
For your purposes, an example /happy/go/path/yay/.env file might look like:
export GOPATH="/happy/go/path/yay"
export PATH="$GOPATH/bin:$PATH"
I would write a script which can infer the proper GOPATH from the current directory, and then alias the go command to first call this script. For example, a very simple implementation:
#!/bin/bash
# infer-gopath.sh
pwd
And then, in .bash_aliases (or wherever you keep your aliases):
alias go='GOPATH=$(infer-gopath.sh) go'
This sets GOPATH to whatever infer-gopath.sh outputs just for the invocation of the go command, so it won't have any lasting effect on your shell.
I know this is not very clever but I find that if I simply go to the base directory of the go project where I have the src, pkg and bin folders I can simply type:
export GOPATH=$(pwd)
and thats it all good!
My impression is that the go tool actively discourages "maintaining multiple versions of the same library on a per project basis" for the precise reason that experience has shown that that strategy doesn't work on large codebases (such as Google's). There has been quite a lot of discussion about package versioning on golang-nuts: (search the list), and it seems that the discussion is still open, as indicated by Ian Lance Taylor in this June 6, 2013 interview (search for the word "versioning").
The go packaging system is designed to allow every project to have its own directory structure; the only restriction is that they all need to be children of (some) directory in GOPATH. This has the advantage that it interacts well with version control systems, as long as the VCS master always builds. In the blog interview referenced above, ILT suggests:
What we do internally is take a snapshot of the imported code, and update that snapshot from time to time. That way, our code base won't break unexpectedly if the API changes.
Substituting "my other libraries" for "the imported code", that seems like a possibility; you could have two go directories, production and development; for development, you could put the development directory first in the path so that development binaries and libraries don't pollute the production directories. I don't know if that is sufficient.
If you really want to have a separate GOPATH for each project, I'd suggest the following:
1) Make every project's GOPATH end at a directory named go (or some such)
2) Deduce the GOPATH using the something like the following shell function (almost totally untested):
gopath() {
GOPATH="$(
( while [[ $PWD != / && $(basename $PWD) != go ]]; do
cd ..
done
if [[ $PWD == / ]]; then
echo $GOPATH
else
echo $PWD
fi
))" go "$#"
}
Then you can use gopath instead of go as long as your current working directory is somewhere inside the project's repository. (More sophisticated possibilities might include using the explicitly provided project path, if any, to deduce GOPATH.)
I can't comment, but to build off the answer from #joshlf :::
alias go by prepending the GOPATH -- my method doesn't require you deal with/create an extra file:
alias go='GOPATH=$(echo $(pwd)) go'
cheers
I like the guts of the gopath() answer above, but I don't like (a) having the GOPATH set only for the go command (I use it in vim plugins, etc), nor do I like (b) having to remember to type gopath instead of go :)
This is what I ultimately added to my ~/.bash_profile:
export GOPATH="... a default path ..."
function cd() {
builtin cd $# &&
export GOPATH="$(
( while [[ $PWD != / && $(basename $PWD) != go ]]; do
cd ..
done
if [[ $PWD == / ]]; then
echo $GOPATH
else
echo $PWD
fi
))"
}
(I also wrote up the above with a little extra discussion of requirements in this blog post)
I'm a Golang newb, but the best way to do this would probably be to create a shell script in each of your projects, to build/run your project, and put this script in your project root :)
#!/usr/bin/env bash
// get absolute path to the directory which contains this script
PROJECT_DIR=$(cd $(dirname $0) && pwd)
// set GOPATH as you wish, then run go build
GOPATH=${GOPATH}:${PROJECT_DIR} && cd ${PROJECT_DIR} && go build
this script should work, even if you execute it from a directory that is not the project root.

Resources