copy multiple files to null directory - shell

I want to check if source file exist, so I assign TO_DIR=/dev/null in my gnu makefile.
APPS=a b c d
install:
cp $(APPS) $(TO_DIR)
for normal case, I'll run 'TO_DIR=~/bin make install'
for test case(just make sure file exist for copy), I'll run 'TO_DIR=/dev/null make install'
But it will failed, because /dev/null is not a pseudo directory.
Is there better solution?

You could do another make target, like this:
check:
file $(APPS) > /dev/null
file utility will check the existence of all the files and fail when any of these does not exist. Its output is excessive for this task, so we pipe it to /dev/null
You will run check like this:
make check

Here's a modification of spacediver's answer that just branches based on the value of TO_DIR
install:
[ -z "$(TO_DIR)" ] && make install_for_real || make check
install_for_real:
cp $(APPS) $(TO_DIR)
check:
file $(APPS) > /dev/null
This should do the check for either of
make install
make install TO_DIR=
but the real installation for
make install TO_DIR=/path/to/installation/dir

Related

build command need to be executed inside a specific named directory "./package" from child folder or parent folder script terminal (cd) loop or regex

how to write cd NAME_FOLDER and recursively search for it by name and then go to it if necessary (see below for more details).
the NAME_OF_FOLDER is unique so no worried about that.
but the challenging things are:
that it needs to search from the PARENT to the CHILD,
and if it didn't find it that way, search from CHILD to PARENT.
(or you can use any other logic if you think my logic is slow)
example folder
here is an example image of my folder:
possible scenarios
here are some scenarios:
if I am inside
./package -> don't run cd
./test -> cd ./package
./src -> cd ../ && cd ./package
./lib -> cd ../../ && cd ./package
and so on for every deep folder structure
docs:
../ means go from child to parent
./ means go from parent to child
why do I need it?
I am a javascript developer,
and I am using the sveltekit framework
to create a svelte package library.
and I need to publish that library to npm.
and this is ok.
but since I write a lot the same CLI codes.
I am changing the package.json's scripts object,
so I write only one time npm run build to run 6+ commands
{
...
"build": "
svelte-kit sync
&& svelte-package
&& npm version patch
&& cd ./package # only this I need to solve this (the others are solved)
&& npm publish
&& git commit
"
}
this is in one line, but for making you read it easily the code in multiple lines
here how it is in my code:
what does the build command should do?
the command generates a ./package folder always on the root of the folder
(where we can find package.json, .gitignore, ./src, etc...)
increase the number of versions automatically when we use the build command,
then... TODO:
do the script I need to access the ./package folder from every folder I am in now. (like cd ./package)
npm publish
my os?
windows 11 (but using bash with vscode) or also powershell will be good but prefer bash
any other details, I will tell you. thanks
For testing, I created this structure:
test/
package/
src/
lib/
routes/
Then I created that script:
#!/bin/bash
topdir="test"
while [[ $(basename "$(pwd)") != "$topdir" ]]
do
if [[ -d package ]]
then
cd package
pwd
else
cd ..
pwd
fi
done
if [[ -d package ]]
then
cd package
pwd
fi
This script "climbs" the directories until it finds a "package" directory. It the cd into it.
To use the script, you have to source it. If you execute it, it will change directories while the script is running, but it will not affect your current terminal.
So, lets assume the script is ~/bin/cd_package.bash
You would call it like this: . ~/bin/cd_package.bash
Note the pwd commands are just so you can follow what is going on and can be removed once you are convinced it works.

Makefile rebuilds projects on source update

Is there a way to have a 'watch' target in my Makefile which would keep looping and rebuilding the project every time the source file gets changed?
I have this for my Latex project:
.PHONY : monitor
monitor:
while true; do \
inotifywait -e modify -q *.tex *.cls; make all; \
done
Interesting arguments:
-q for quiet
-r for recursive (if you want to watch the whole src folder)
-e to list specific events (if your editor does more file operations and retriggers the build way too often)
--exclude to exclude some (if your src folder contains build artifacts) to make sure the build itself will not retrigger this loop (which would be equivalent of an infinite loop without any delays)
More arguments here (inotify tools are amazing):
https://linux.die.net/man/1/inotifywait
Depending on your distribution you might have to install separate package, on my Debian I had to do
sudo apt-get install inotify-tools

How to use GNU Makes dry run mode with CMake generated Makefile before actually performing the installation

Every now and then a new tarball or a new xyHub/Lab-repository needs to be built. They usually come with a Makfile or an Autotools/CMake/XY-Generator provides one on the fly. As the maintainers most likely use another operating system or distribution than the one I am currently running, the assumptions that went into their Makefiles usually do not fit my filesystem hierarchy (lib vs. lib64, bin vs. sbin, /usr/lib vs. /lib and so on). As the final command in the build sequence usually is
sudo make install
it is quite annoying to move thousands of files to the correct place. Or even worse determine which files of my distribution were overwritten. Here GNU Makes dry run mode comes in very handy. Running
sudo make -n install
first, saves me the trouble of cleaning up my file system, by just printing all the commands from all active GNU Make recepies without executing them. In case of a handwritten or Autotools-generated Makfile this works as intended. If the Makefile contains something like:
#PREFIX is environment variable, but if it is not set, then set default value
ifeq ($(PREFIX),)
PREFIX := /usr/local
endif
install: unixlib.a
install -d $(DESTDIR)$(PREFIX)/lib/
install -m 644 unixlib.a $(DESTDIR)$(PREFIX)/lib/
install -d $(DESTDIR)$(PREFIX)/include/
install -m 644 unixlib.h $(DESTDIR)$(PREFIX)/include/
I would see exactly what would happen. Every install/cp/mv-command with the full path information would be printed. If I made a mistake with the install prefix in the configure step I can see it there. If the default in the Makefile is weird because it comes from another OS, I would see it there.
Now in case of a CMake-generated Makefile this is different. Doing
mkdir build && cd build
cmake ..
make
sudo make -n install
only produces output that ends in
...
make -f CMakeFiles/Makefile2 preinstall
/usr/bin/cmake -E cmake_echo_color --switch= --cyan "Install the project..."
/usr/bin/cmake -P cmake_install.cmake
As these commands get not executed, just printed, I do not get all the cp/mv/mkdir/install/etc-commands that I would like to see first, before I let the Makefile touch the file system.
Is there a way to get the list of commands that would be executed from the install target in a CMake-generated Makefile as it is the case with handwritten or Autotools-generated ones?
Is there a way to get the list of commands that would be executed from the install target.
Actually, the core part of installation process is contained in the file cmake_install.cmake (which is created in the build directory). This file is processed as CMake script using cmake -P flow of the cmake executable.
The script cmake_install.cmake performes installation of files with install command. Semantic of the install command, used by the script, differs from the one described in documentation: internally, CMake uses some undocumented features of the command.
But it shouldn't be so hard to understand cmake_install.cmake script in general and deduce paths from it.

Dynamic target for root or nested directory

My makefile contains a target for node_modules directory, which depends on package.json file:
node_modules: package.json
npm install && touch "$#"
My current project happens to have a "sub-package" with its own set of dependencies. I modify the target above to the following:
%/node_modules: %/package.json
cd $(shell dirname "$<") && npm install && touch node_modules
Now, I can do make path/to/subpackage/node_modules and make runs the expected npm install command. However, I no longer seem to be able to do make node_modules - make exits with status code 0 and message Nothing to be done for 'node_modules'.
This indicates make will now only run the appropriate command for node_modules folders which exist within a subdirectory.
How can I change the target so that it supports both nested and root node_modules directories within the same target?
In other words, I would like to remove the duplicated target definition because the command to make the root's node_modules directory is the same as the command to make the nested path/to/node_modules directory.
As research, I tried to look at the GNU Make tutorial but could not find any relevant information - possibly because I simply do not know what to look for.
It isn't very elegant, but you could use a "canned recipe":
define MAKE_NODE_MODULES
npm install && touch node_modules
endef
node_modules: package.json
$(MAKE_NODE_MODULES)
%/node_modules: %/package.json
cd $* && $(MAKE_NODE_MODULES)

Wrap a scons build process in a Makefile

I've written a scons build chain form a little C project, but I'm afraid users won't like to be told "You should install SCons first. Besides, it's really cool!" (expecially my professor, as he's kind of from the old guard).
Is there a way I can set up a Makefile that will wrap scons, not requiring it to be installed on the target system?
After looking for such a solution some time ago, I ended up writing a Makefile for this purpose.
Because SCons also comes as a drop-in userspace package scons-local (see the download page), one can fetch in and run it. Here is a dissectioned and commented version of my Makefile, which I also uploaded as a gist.
all: get_scons
#$(SCONS_EXE)
↑ The default action depends on scons being available, and simply runs the scons command (set later in the script) (the # symbol prevents make from printing the command)
SCONS_VERSION=2.3.4
scons-local-%.tar.gz:
curl -L http://sourceforge.net/projects/scons/files/scons-local/$(SCONS_VERSION)/scons-local-$(SCONS_VERSION).tar.gz > scons-local-$(SCONS_VERSION).tar.gz
touch scons-local-$(SCONS_VERSION).tar.gz
scons-local: scons-local-$(SCONS_VERSION).tar.gz
mkdir -p scons-local
tar xzf scons-local-$(SCONS_VERSION).tar.gz --directory scons-local
touch scons-local
↑ Set up the rules for fetching the tarball and unpack it into the scons-local directory
NATIVE_SCONS=$(strip $(shell which scons 2>/dev/null))
ifeq ($(NATIVE_SCONS),)
SCONS_EXE=python2 ./scons-local/scons.py
get_scons: scons-local
#echo "Couldn't find an installation of SCons, using a local copy"
else
SCONS_EXE=$(NATIVE_SCONS)
get_scons:
#echo "Found SCons installation at $(SCONS_EXE)"
endif
↑ Look for the scons executable in the search path (using the which command): if it is available, set up the get-scons target to simply print it is available. If, instead, it is not available, create the get-scons target instructing it to depend on the scons-local target defined earlier.
clean:
$(SCONS_EXE) -c
rm -rf scons-local
rm -f scons-local-*.tar.gz
.PHONY: all clean get_scons
↑ Finally, set-up the clean target that delegates to scons and deletes the local installation afterwards. The .PHONY rule tells make that the following rules do not correspond to files being created.
At this point, one could add more proxy rules of the kind:
mytarget: get_scons
#$(SCONS_EXE) mytarget
Which will invoke scons with the corresponding target.
Hope this is useful, feel free to correct me in case there's something wrong (I'm actually not a Makefile expert, and I'm trying not to become one by using SCons instead :P )

Resources