Makefile recompiling unchanged files - makefile

I'm attempting to get my Makefile(s) to only recompile when files have changed and it works brilliantly for the Java target, but not for JS. There aren't any errors reported, but when I run make bundle the second time around, it still re-runs all of those commands.
The output of running find ... manually seems to produce exactly what is expected, with no extra or missing lines.
JAVA_SRC_DIR := .
JAVA_SOURCES := $(shell find $(JAVA_SRC_DIR) -name '*.java' -o -name '*pom.xml')
JS_SRC_DIR := ./ui-bundle
JS_EXCLUDE := \(node_modules\|coverage\)
JS_SOURCES := $(shell find $(JS_SRC_DIR) -name '*.js' -o -name '*.jsx' -o -name '.babelrc' -o -name 'package.json' | grep -v $(JS_EXCLUDE) | grep -v -e '^$')
bundle: $(JS_SOURCES)
npm install
./node_modules/jest-cli/bin/jest.js --verbose
./node_modules/gulp/bin/gulp.js package
service: $(JAVA_SOURCES)
mvn clean install -pl "!integration"
I'm guessing this has something to do with it, but when I try to find what files make thinks has changed with make -d | grep "\(older\|newer\)" it doesn't find anything, but make -d concludes with:
Finished prerequisites of target file `bundle'.
Must remake target `bundle'.
Additionally make -d seems to try to run some sort of npm command.
Where am I going wrong?

The problem is that bundle is just a name for the rule, and the rule doesn't actually build a file named "bundle". No matter how new or old the prerequisites are, when you make bundle, Make sees that no such file exists, deduces that it must be built, and executes the rule.
If you want Make to refrain from executing the rule when the prerequisites have not changed, you must give it something to compare them to. If you can identify some file that the rule always modifies, you can make that file the target. I can't suggest a candidate because I don't know what npm, jest and gulp do, but suppose it's jest_output:
jest_output: $(JS_SOURCES)
...
Or you could have an actual file called bundle, which serves as a marker for Make, an empty file that has a name and modification time and nothing else:
bundle: $(JS_SOURCES)
npm install
./node_modules/jest-cli/bin/jest.js --verbose
./node_modules/gulp/bin/gulp.js package
touch bundle

Related

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 could I get the full path of a file with file name?

[ SOLUTION ]
Thanks to #oguzismail and #stylo I find the solution. In the find command I modified the 2>&1 for 2>/dev/null and deleted the grep command, but I found the problem that it retrieves me two different path (because Android studio it was generation two different apk with the same name in two different paths).
To only get the path of the apk I want, I add a "filter" for my find command so the solution is:
find / -path ./intermediates -prune -o -name app-ipd-debug.apk 2>/dev/null
with name of the folder ./intermediates and -prune -o I can get the path I wanted to.
I saw the solution in this post
[ PROBLEM ]
I am doing a shell script that build an android project, install apk and do some more configurations in the device like set owner device owner (is a kiosk mode app) and some more stuff.
Now I am trying to dynamically build the project and get the apk file to install in the device but it doesn't work correctly.
I try putting the full path as a variable in my shell script and this works installing the app using the command adb install:
adb install -t -r $APK_PATH
I have tried t get the APK_PATH with find command but it retrieves me a lot of output that I don't know how to handle it, the command is :
find / -name apk-file-name.apk
A lot of output with "Permission denied" and "Operation not permitted" is shown and in one line of those the apk path is shown (this is the one I don't know how to get it, only this result)
I try to filter the results using grep but it doesn't work
find / -name apk-file-name.apk 2>&1 | grep -v "Operation not permitted"
and
find / -name apk-file-name.apk 2>&1 | grep -v "Operation not permitted"|"Permission denied"
any help?
You can use the locate command to locate files in your file system.
if you haven't enabled it yet, you can do so by running the following command
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist
you won't be able to use it for a few minutes since it will index everything and later on you can use locate file_name.apk to find the file you're looking for.

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.

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 )

How can CMake be used to generate Makefiles with personalized commands?

I like to keep my Makefiles flexible and multifunctional. One of the tasks I usually add to make command is tar, for example the following instruction does the job:
tar:
tar -cvf $(PROGNAME).tar $(SRCS) Makefile
My question is: How can CMake be used to generate personalized commands like tar?
I would like to see some code samples.
For the full functionality it would be useful to create project's components and be able to use them as parameters.
(Exempli gratia: archive only header files or some specific library).
Thanks in advance for your answers!
The literal translation of your tar example would be:
ADD_CUSTOM_TARGET(tar
tar -cvf ${CMAKE_CURRENT_BINARY_DIR}/${PROGNAME}.tar ${SRCS} Makefile
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
This adds a new target "tar" that always executes the given command whenever it is requested as a command line target, i.e. whenever you run make tar it will create a new tar file. The WORKING_DIRECTORY argument will ensure that the source files are taken from the source directory, while CMAKE_CURRENT_BINARY_DIR ensures the output goes in the current build directory.
A slightly better iteration would be to replace tar with ${CMAKE_COMMAND} -E tar, as this doesn't depend on the command line tar program being available. So something like this would tar up all the header files when you run make tar:
SET(HEADER_FILES my.h another.h)
SET(PROGNAME myprog)
ADD_CUSTOM_TARGET(tar ${CMAKE_COMMAND} -E tar -czvf
${CMAKE_CURRENT_BINARY_DIR}/${PROGNAME}.tar.gz ${HEADER_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
An even better iteration would be to use the CPack features to create source or binary tar files, but that's quite a bit more work and may not be what you need anyway.

Resources