Calling a shell script from a makefile? - node.js

I have a custom shell script to make twitter bootstrap from source and then move the files to my node.js app's /lib file:
rm -r bootstrap
make bootstrap
mv -f bootstrap/css/* ../../lib/public/css
mv -f bootstrap/img/* ../../lib/public/img
mv -f bootstrap/js/* ../../lib/public/js
Running this from the shell works just fine using ./make_bootstrap.sh
Now I've created a Makefile for my full app (mainly compiling coffeescript and easy test initialization) and want to have a command that executes this custom shell script to build bootstrap. Here is my makefile
REPORTER = spec
all: build
build:
#./node_modules/coffee-script/bin/coffee \
-c \
-o lib src
bootstrap:
#./src/bootstrap \
./make_bootstrap.sh
clean:
rm -rf lib
mkdir lib
watch:
#./node_modules/coffee-script/bin/coffee \
-o lib \
-cw src
test:
#./node_modules/mocha/bin/mocha \
--reporter $(REPORTER) \
test/*.coffee
.PHONY: build bootstrap clean watch test
with the relevant command being 'make bootstrap'. However when I run make bootstrap from the command line all I get is this error:
make: ./src/bootstrap: Permission denied
make: *** [bootstrap] Error 1
Originally I had assumed that it was a permission error but even setting all permissions on files (chmod 777) results in nothing. Files I have given full permissions at this point include the root Makefile, my custom shell script in the bootstrap folder and the makefile within the bootstrap folder itself.

EDIT:
Based on the comments I have refactored to this
bootstrap:
rm -r src/bootstrap/bootstrap
$(MAKE) -C ./src/bootstrap bootstrap
mv -f src/bootstrap/bootstrap/css/* lib/public/css
mv -f src/bootstrap/bootstrap/img/* lib/public/img
mv -f src/bootstrap/bootstrap/js/* lib/public/js
This duplicates the functionality of the shell script I had before (moving files for my custom project) and still uses the standard makefile that Twitter Bootstrap ships with. Much cleaner... I'm going to live the original answer below so people can see the evolution and refactor.
OLD ANSWER
Ok thank you guys in the comments for pointing my in the right direction. This solution works:
bootstrap:
cd ./src/bootstrap; \
./make_bootstrap.sh
What happens is it executes the change directory (in a sub process so it doesn't affect where I run make from) and then executes the custom script. It seems as if I probably shouldn't be using something like this in a makefile since it feels 'dirty'; perhaps a more clean way to do it would be to invoke the LESS compiler myself and mimic the makefile provided by bootstrap. I'm using this for a tiny personal project though so it does the job.

Related

Option to get the rust project dir in shell script?

I am writing some shell scripts in a rust project like this to auto-generate the schema and model files:
#!/usr/bin/env bash
set -u
set -e
set -x
diesel_ext --derive Insertable,Queryable,Debug,Serialize,Deserialize,Default \
--add-table-name \
--import-types "rocket::serde::Serialize" \
--import-types "serde::Deserialize" \
--import-types "crate::model::diesel::dolphin::dolphin_schema::*" \
--schema-file src/model/diesel/dolphin/dolphin_schema.rs --model > src/model/diesel/dolphin/dolphin_models.rs
This script work fine but I could not easily move the shell script when refactoring the project code. Sometimes I need to move the shell script to another folder and make the code file structure more clear and understandable.
Without knowing the project dir, I could not move the shell script because when I move it, I have to change the folder relative path in the shell script.
Is it possible to get a project dir to make this work more smoothly? Just like the Gradle project.dir.
cargo locate-project will output a JSON description of the path to the project's Cargo.toml file, which you can pipe through jq and process with dirname to find the directory.
You could then give this to e.g. cd to switch directories.
cd "$(dirname "$(cargo locate-project | jq -r .root)")"
(There may be error conditions that need to be handled.)

Extract string from path/dir using Make

I’ve a Makefile with multiple git repo’s which I need to clone
I use the following which works
clone:
git clone https://github.company.corp/dev-wi/ws-led.git
git clone https://github.company.corp/dev-wi/tools-extension.git
git clone https://github.company.corp/dev-wi/javt-ra.git
While the following code works, I want to do something like this in loop for all the repos on the list
build:
cd ws-led; \
docker build -t ws-led .
cd tools-extension; \
docker build -t tools-extension .
...
For each repo I need to change dir
And run the docker build ,
I want to avoid doing this over and over again ...
I know that I need to extract the string after /dev-wi/ as this is the repo directory which I need to run docker build on.
since I’ve many repo how can I do it easily ?
I try with subset however I have also the git command (in clone) so it doesn't work,any idea?
update
I've created the A new makefile and use only this code (the ws-led and tools-extension is folders in the same level of of the makefile
repos := ws-led tools-extension
.PHONY: all
all: $(patsubst%,%/docker-build.log,$(repos))
%/docker-build.log: %/.git
cd $*; docker build -t $* . >&2 | tee docker-build.log
I got error:
make: Nothing to be done forall'.`
what am I missing here ?
I try to simplify it but removing the git and let say that the folders (repo) existing on the same level of the makefile
UPDATE
Im change the makefile to be under the root
proj
- ws-led
— Dockerfile
-tools-ext
—Dockerfile
-Makefile
I try with the following
all: pre docker-build
.PHONY: pre docker-build
repos := ws-led tools-ext
pre:
$(patsubst %,%docker-build,$(repos))
docker-build:pre
cd $*; docker build -t $* . >&2 | tee docker-build
when I run make I got the following error
ws-leddocker-build ws-leddocker-build
make: ws-leddocker-build: No such file or directory
Any idea?
Looping is generally something you want to avoid. Instead, declare a series of targets for each repo.
repos := ws-led tools-extension javt-ra
.PHONY: all clone
all: $(patsubst %,%/.built,$(repos))
clone: $(patsubst %,%/.git,$(repos))
%/.built: %/.git
cd $*; docker build -t $* .
touch $#
%/.git:
git clone https://github.company.corp/dev-wi/$*.git
The .built flag file is a bit of a wart, and could usefully be replaced with something more useful, like the output from docker build.
all: $(patsubst %,%/docker-build.log,$(repos))
%/docker-build.log: %/.git
cd $*; docker build -t $* . >&2 | tee docker-build.log
The reason we generally try to avoid loops is to allow make to do its primary job properly -- avoid rerunning commands when a target is already up to date. So, for example, if you only changed ws-led, you don't want to force the other two to be rebuilt as well.
Having said that, the $(patsubst ...) is a loop of sorts; it basically loops over repos and creates a small piece of text around each. Without the patsubst we could write
all: ws-led/.built tools-extension/.built javt-ra/.built
which simply says that to make all we need to make those three; and then
%/.built: %/.git
says that for anything matching the pattern, it depends on the same stem with /.git after it. So in an otherwise empty directory, make would find that
to make all, we need to make ws-led/.built, tools-extension/.built, and javt-ra/.built;
to make ws-led/.built, we need to make ws-led/.git;
to make ws-led/.git, we need to
git clone https://github.company.corp/dev-wi/ws-led.git
then once this prerequisite is satisfied,
cd ws-led; docker build -t ws-led .
touch ws-led/.built
to make tools-extension/.built, we need to make tools-extension/.git;
to make tools-extension/.git, we need to
git clone https://github.company.corp/dev-wi/tools-extension.git
... etc etc.
In the future, when make finds that ws-led is newer than ws-led/.built it will build it again; but if it is not, it will conclude that no work needs to be done, etc for the other targets. This is how we avoid building things needlessly; but it obviously requires that the Makefile properly contains a formalization of every relevant dependency. (In this case, you would ideally like for there to be a way to know when the Git upstream has changed and something needs to be pulled by the local Makefile; this currently simply regards everything as done if the local Git clone has not received any updates.)

"No such file or directory" while in link path

When compiling, I always place the build in a separate directory. For example:
mkdir build
cd ./build
(cd ..; ./bootstrap)
../configure
make
Since I have plenty of RAM the aim is to compile on a TMPFS.
The script gets the name of the project, uses it for the name for the directory created in $XDG_RUNTIME_DIR/build and finally links it.
# setup-build.sh
#!/usr/bin/bash
set -e
my_project_name=$(basename $(pwd))
my_project_build_dir="$XDG_RUNTIME_DIR/build/$my_project_name"
mkdir -p $my_project_build_dir
ln -s "$my_project_build_dir" "$(pwd)/build"
The script runs without a problem. But, when I do cd ./build; ../configure it returns an error: bash: ../configure: No such file or directory. The file most certainly does exist, but Bash can't find it!
I altered the script to this:
#!/usr/bin/bash
set -e
my_project_src_dir="$(pwd)"
my_project_name="$(basename $(pwd))"
my_project_build_dir="$XDG_RUNTIME_DIR/build/$my_project_name"
mkdir -p "$my_project_build_dir"
ln -s "$my_project_build_dir" "$(pwd)/build"
cd "$my_project_build_dir"
echo "$my_project_src_dir" > "./project-src-dir.txt"
To compile I have to type cd ./build; $(cat ./project-src-dir.txt)/configure; make. This causes Bash complete to partial break, though. As in I can't TAB complete file names from $my_project_src_dir with this method, but TAB completion for arguments works fine. Ifautoconf is needed: (cd $(cat ./project-src-dir.txt); ./bootstrap). If anyone has any other ideas I would still prefer to be able to just do ../configure, although this will have to do for now.
Edit: Had to change my_project_name="$(basename '$my_project_src_dir') to my_project_name="$(basename $(pwd))" as it was taking '$my_project_src_dir' literally.

Symbolic link to a hook in git

I wrote my own custom post-merge hook, now I added a "hooks" directory to my main project folder (since git doesn't track changes in .git/hooks), somewhere I read that I can make a symbolic link from hooks to .git/hooks so I don't have to copy the file from one folder to the other every time someone changes it so I tried:
ln -s -f hooks/post-merge .git/hooks/post-merge
But it doesn't seem to work, any ideas why? "ln hooks/post-merge .git/hooks/post-merge" works fine but making a hard link is the same as copyin I guess....
you just used wrong path, it should be:
ln -s -f ../../hooks/post-merge .git/hooks/post-merge
While you can use symbolic links, you can also change the hooks folder for your project in your git settings with :
git config core.hooksPath hooks/
Which is local by default so it won't ruin git hooks for your other projects. It works for all hook in this repository, so it's especially useful if you have more than one hook.
If you already have custom hooks in .git/hooks/ that you do not want to share with your team you can add them in hooks/ and add a .gitignore so they're not shared.
Changing directory before linking
cd /path/to/project-repo/.git/hooks
ln -s -f ../../hooks/post-merge ./post-merge
The path calculation is done relative to the symlink. Let's understand using an example,
ln -s path/to/file symlink/file
Here, the path to the file should actually be the relative path from the symlink path.
The system actually calculates the file path as symlink/path/path/to/file
The above command should be re-written as
ln -s ../path/to/file symlink/path
The folder structure being,
/code
------ symlink/file
------ path/to/file
Utilizing Michael Cihar's comment, here is an example of a bash script I wrote to simply create these symlinks. This script is located in git_hooks/ dir which is at the project root. My .git/ folder is also in the same directory level.
#!/usr/bin/env bash
pwd=$(pwd);
# Script is designed to be ran from git_hooks/ dir
if [[ "$pwd" == *"git_hooks"* ]]; then
files=$(ls | grep -v -e '.*\.');
while read -r file; do
ln -s ../../git_hooks/$file ../.git/hooks/
echo "Linked $file -> ../.git/hooks/$file"
done <<< "$files";
else
echo "";
echo "ERROR: ";
echo "You must be within the git_hooks/ dir to run this command";
exit 1;
fi
My script must be ran from within the actual git_hooks/ directory. You can modify it to behave differently, if you'd like.
This script will symlink any file that is not suffixed with a file extension within the git_hooks/ directory. I have a README.txt in this directory + this script (named symlink.sh). All the actual git hooks are named 'pre-commit', 'pre-push', etc. so they will be symlinked.
why not just
cp ./hooks/* .git/hooks/
this worked for me in Mac OS

In Unix, can I run 'make' in a directory without cd'ing to that directory first?

In Unix, can I run make in a directory without cd'ing to that directory first?
make -C /path/to/dir
As noted in other answers, make(1) has a -C option for this; several commands have similar options (e.g. tar). It is useful to note that for other commands which lack such options the following can be used:
(cd /dir/path && command-to-run)
This runs the command in a sub-shell which first has its working directory changed (while leaving the working directory of the parent shell alone). Here && is used instead of ; to catch error cases where the directory can not be changed.
If the reason you don't want to cd to a directory is because you need to stay in the current directory for a later task, you can use pushd and popd:
pushd ProjectDir ; make ; popd
That goes into the ProjectDir, runs make, and goes back to where you were.
Also you may use:
make --directory /path/to/dir
makefile:
all:
gcc -Wall -Wpedantic -std=gnu99 -g src/test.c -o build/test
run:
./build/test
or
run:
./../build/test
etc.

Resources