I have been used premake for a while. When I have to run small script or something which seems to be unrelated to build phase(e.g. debug, packaging, build for external libraries etc...), I just used Makefile project for that like below
-- [[ X. External Makefile libraries ]]
project "external"
kind "Makefile"
location "build"
buildcommands {
"cd ..; make -f Makefile"
}
cleancommands {
"cd ..; make -f Makefile clean"
}
-- [[ X+1. Integration ]]
project "integration"
kind "Makefile"
location "build"
buildcommands {
-- PacketNgin Application Library
"ar x ../libc.a",
"ar x ../libm.a",
"ar x ../libtlsf.a",
"ar x ../libcore.a",
"ar x ../libexpat.a",
"ar rcs ../libpacketngin.a *.o",
"cp -rL ../core/include/* ../../include",
"cp -rL ../expat/include/* ../../include",
"cp -rL ../openssl/include/* ../../include",
"cp -rL ../zlib/*.h ../../include",
"rm ./*.o -rf",
-- Linux Application library
"ar x ../libtlsf.a ", -- Blank is added at the end on purpose
"ar x ../libcore_linux.a",
"ar rcs ../libumpn.a *.o",
"rm ./*.o -rf ", -- Blank is added at the end on purpose
}
cleancommands {
"rm *.o -rf",
"rm ../*.a -rf"
}
I realize this practice is very confused since it does not separates real build Makefile from just phony targets, even make unecessary Makefiles for build. So, I want to figure out generate phony target by premake.
I considered newaction syntax but I found it just make target for premake script rather than Makefile target.
Is there any best practice or way to generate phony target by premake?
Creating arbitrary phony targets is not supported by Premake at this time (though you can submit a feature request or tackle it yourself and create a pull request).
But you could use Premake itself to run the commands for you. Here's a simple example that creates a new action called "integrate":
function executeAll(commands)
for _, command in ipairs(commands) do
os.execute(command)
end
end
newaction
{
trigger = "integrate",
description = "Run integration steps",
execute = function ()
executeAll {
"ar x ../libc.a",
"ar x ../libm.a",
"ar x ../libtlsf.a",
"ar x ../libcore.a",
"ar x ../libexpat.a",
"ar rcs ../libpacketngin.a *.o",
-- and so on...
}
end
}
Once added to your project script, you can call it like:
$ premake5 integrate
Related
I am trying to build https://github.com/wallix/redemption, which uses bjam. I don't really know bjam (and I think, given that I already don't really know make and cmake, I don't really have that much space left in in my head for yet another build system language).
More specifically, I want to build projects/qtclient in there, as part of the main project; so that when I run tools/packager.py (which repeats the bjam build process from scratch), i also get the qtclient executable in the generated .deb.
Note that, when I'm the root directory of the repository, for the main build, I can do:
redemption.git$ bjam exe libs
warning: No toolsets are configured.
warning: Configuring default toolset "gcc".
warning: If the default is wrong, your build may not work correctly.
warning: Use the "toolset=xxxxx" option to override our guess.
warning: For more configuration options, please consult
warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html
...patience...
...found 2502 targets...
...updating 267 targets...
gcc.compile.c++ bin/gcc-9/debug/log.o
...
... and it works. If I try to build the projects/qtclient from the same directory location, it fails:
redemption.git$ bjam projects/qtclient/
warning: No toolsets are configured.
warning: Configuring default toolset "gcc".
warning: If the default is wrong, your build may not work correctly.
warning: Use the "toolset=xxxxx" option to override our guess.
warning: For more configuration options, please consult
warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html
redemption-src: /home/USER
/home/USER/jam/redemption-config.jam: No such file or directory
/home/USER/jam/defines.jam: No such file or directory
Assume Qt5. (bjam -s qt5)
projects/qtclient/Jamroot:48: in modules.load
ERROR: rule "setvar" unknown in module "Jamfile</home/USER/src/redemption_git/projects/qtclient>".
/usr/share/boost-build/src/build/project.jam:372: in load-jamfile
/usr/share/boost-build/src/build/project.jam:64: in load
/usr/share/boost-build/src/build/project.jam:142: in project.find
/usr/share/boost-build/src/build/targets.jam:453: in find-really
/usr/share/boost-build/src/build/targets.jam:475: in class#project-target.find
/usr/share/boost-build/src/build-system.jam:724: in load
/usr/share/boost-build/src/kernel/modules.jam:295: in import
/usr/share/boost-build/src/kernel/bootstrap.jam:139: in boost-build
/usr/share/boost-build/boost-build.jam:8: in module scope
... but if I change directory to projects/qtclient first, and call bjam qtclient (or just bjam) there, it works:
demption.git/projects/qtclient$ bjam qtclient
redemption-src: /home/USER/src/redemption_git
Assume Qt5. (bjam -s qt5)
warning: No toolsets are configured.
warning: Configuring default toolset "gcc".
warning: If the default is wrong, your build may not work correctly.
warning: Use the "toolset=xxxxx" option to override our guess.
warning: For more configuration options, please consult
warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html
warning: non-free usage requirements <threading>multi ignored
warning: in main-target QtCore at Jamroot:146
...patience...
...found 2454 targets...
...updating 107 targets...
qt5.moc bin/gcc-9/release/src/qt_input_output_api/moc_qt_input_output_clipboard.cpp
...
gcc.link bin/gcc-9/release/qt5client
...updated 107 targets...
Right - so this shows me, that the error 'ERROR: rule "setvar" unknown in module "Jamfile</home/USER/src/redemption_git/projects/qtclient>".' is due to bjam running in the "project root" directory, and not in the projects/qtclient subdirectory.
So, now I try to integrate the build of projects/qtclient inside the main Jamroot; I've tried adding qtclient at end of alias exe line (before the semicolon):
alias exe : rdpproxy rdpclient rdpinichecker qtclient;
Result: build does not even start:
redemption.git$ bjam libs exe
error: Unable to find file or target named
error: 'qtclient'
error: referred to from project at
error: '.'
And I've tried adding projects/qtclient at end of alias exe line :
alias exe : rdpproxy rdpclient rdpinichecker projects/qtclient;
Result: build starts, but fails as bjam does not run in the project subdirectory (same error: 'ERROR: rule "setvar" unknown in module'):
redemption_git$ bjam libs exe
warning: No toolsets are configured.
...
/home/USER/jam/redemption-config.jam: No such file or directory
/home/USER/jam/defines.jam: No such file or directory
Assume Qt5. (bjam -s qt5)
projects/qtclient/Jamroot:48: in modules.load
ERROR: rule "setvar" unknown in module "Jamfile</home/USER/src/redemption_git/projects/qtclient>".
/usr/share/boost-build/src/build/project.jam:372: in load-jamfile
/usr/share/boost-build/src/build/project.jam:64: in load
...
As per Boost-build - dependency on subproject target I also tried modifying the code so it has this, while restoring the original alias exe line:
import feature ;
feature.feature qtclient : : dependency free ;
project redemption
: requirements
$(REDEMPTION_CXXFLAGS)
$(REDEMPTION_FFMPEG_FLAGS)
$(REDEMPTION_BOOST_STACKTRACE_FLAGS)
$(GCOV)
<cxxflags>-fno-rtti
<toolset>gcc:<cxxflags>-pipe
# <cxx-lto-default>on
# <cxx-stack-protector-default>on # strong, all
# <cxxflags>-fpie
<qtclient>projects/qtclient//qtclient
: default-build release
;
...
alias exe : rdpproxy rdpclient rdpinichecker ;
... and again, build starts, but fails as bjam does not run in the project subdirectory (same error: 'ERROR: rule "setvar" unknown in module'):
$ bjam libs exe
warning: No toolsets are configured.
...
redemption-src: /home/USER
/home/USER/jam/redemption-config.jam: No such file or directory
/home/USER/jam/defines.jam: No such file or directory
Assume Qt5. (bjam -s qt5)
/home/USER/src/redemption_git/projects/qtclient/Jamroot:48: in modules.load
ERROR: rule "setvar" unknown in module "Jamfile</home/USER/src/redemption_git/projects/qtclient>".
/usr/share/boost-build/src/build/project.jam:372: in load-jamfile
...
Also, as per How to build multiple targets with Boost and Jamroot? I've tried adding reference to projects/qtclient/Jamroot at end of alias exe line:
alias exe : rdpproxy rdpclient rdpinichecker projects/qtclient/Jamroot;
This actually runs and build completes without errors - however there is no mention of "qtclient" anywhere in the build log, and the corresponding executable does not get generated.
The same happens for build-project that I got from bjam - how to always execute shell script before building a target?:
build-project projects/qtclient//qtclient ;
... or for
build-project projects/qtclient ;
I also tried to cheat and just call from the shell:
Echo [ SHELL "cd projects/qtclient; bjam release qtclient" ] ;
... this indeed builds qtclient - but then erases the build folder, before starting to build the main project :(
So - how do I have bjam compile an extra project in a subfolder, as part of the main project, correctly (meaning that bjam would change the current working directory to the subfolder, before attempting to build the extra project)?
EDIT: the procedure described below, while it adds the subproject executable so it runs correctly after installing, it also messes up paths for executables from the main project. A correct procedure has been provided by developers of the original project in this comment:
But if you want, you can add cd projects/qtclient; bjam qtclient / cd projects/qtclient; bjam install in packaging/template/debian/rules, add the dependencies in the packaging/targets/ files and the new executable in packaging/template/debian/redemption.install (the last step may be useless thanks to %PREFIX%/bin/*).
Right, so this was a more complicated thing, and I came to a nasty workaround - but, better than nothing. There were several problems here - first, let's look at the Jamroot of the subfolder project; I'll post this as a diff:
diff --git a/projects/qtclient/Jamroot b/projects/qtclient/Jamroot
index 2379580db..d72e3edb5 100644
--- a/projects/qtclient/Jamroot
+++ b/projects/qtclient/Jamroot
## -1,6 +1,13 ##
-REDEMPTION_PUBLIC_PATH ?= [ SHELL "readlink -n -f ../.." ] ;
+#REDEMPTION_PUBLIC_PATH ?= [ SHELL "readlink -n -f ../.." ] ;
-ECHO "redemption-src: $(REDEMPTION_PUBLIC_PATH)" ;
+path-constant MYPATH : . ;
+REDEMPTION_PUBLIC_PATH ?= [ SHELL "readlink -n -f $(MYPATH)/../.." ] ;
+
+ECHO "qtclient Jamroot: MYPATH $(MYPATH)" ;
+ECHO "qtclient Jamroot: REDEMPTION_ROOT_PATH $(REDEMPTION_ROOT_PATH:E=not_set)" ;
+ECHO "qtclient Jamroot: REDEMPTION_PUBLIC_PATH $(REDEMPTION_PUBLIC_PATH)" ;
+ECHO "qtclient Jamroot: .all-features $(.all-features:E=not_set)" ;
+#ECHO [ SHELL "pstree -s -p $$ && echo PID $$ PWD $PWD" ] ;
JAM_INCLUDE_PATH ?= $(REDEMPTION_PUBLIC_PATH)/jam ;
REDEMPTION_INCLUDE_PATH ?= $(REDEMPTION_PUBLIC_PATH)/include ;
## -124,7 +131,7 ## add_obj graphics.o : src/qt_input_output_api/graphics.cpp ;
local requirement_list = ;
# generated by `bjam targets.jam` from redemption project
-include redemption_deps.jam ;
+include $(MYPATH)/redemption_deps.jam ;
constant EXE_DEPENDENCIES :
Originally, it tried to find REDEMPTION_PUBLIC_PATH via [ SHELL "readlink -n -f ../.." ] ; - and if a shell is being called with readlink -f ../.., it has no other choice but to refer to its CWD; it does not "know" whether it is in a subfolder of a project. Luckily, it turns out there is a bjam syntax that lets a jam file find its own path: the path-constant rule (boost-build/bjam constant for path to Jamroot), and that is what the above change uses, to provide a correct reference to _INCLUDE_PATHs and included files - which gets rid of the "ERROR: rule "setvar" unknown in module".
Once this is done, the next problem is, that if the build of the subfolder project is called from the main Jamfile (e.g. via build-project), we'll get a new error:
/usr/share/boost-build/src/build/feature.jam:140: in feature from module feature
error: feature already defined:
error: in feature declaration:
error: feature "<cxx-color>" : "_" "default" "auto" "never" "always" : "propagated"
The problem is that both the main Jamroot and the subproject Jamroot do include $(JAM_INCLUDE_PATH)/cxxflags.jam ; etc; I tried removing those manually from the subproject Jamroot, for the case when build is started from the main folder - but then eventually we get to the subproject jam complaining about unknows stuff:
/usr/share/boost-build/src/kernel/modules.jam:107: in modules.call-in
ERROR: rule "Jamfile</home/USER/src/redemption.git/projects/qtclient>.flags" unknown in module "Jamfile</home/USER/src/redemption.git/projects/qtclient>".
... so it still needs those files, but they are already in conflict ... so this way won't work.
So again we have to go back to "changing directory", and apparently we must do that from the shell ... So that part is in the main Jamroot, again shown as a diff:
diff --git a/Jamroot b/Jamroot
index eb4188fdd..e4a756002 100644
--- a/Jamroot
+++ b/Jamroot
## -46,6 +46,27 ## JAM_INCLUDE_PATH ?= jam ;
#
#############################
+path-constant REDEMPTION_ROOT_PATH : . ;
+ECHO "main: REDEMPTION_ROOT_PATH $(REDEMPTION_ROOT_PATH)" ;
+import modules ;
+tbuildcmd = "" ;
+local args = [ modules.peek : ARGV ] ;
+if clean in $(args)
+{
+ tbuildcmd = clean ;
+}
+else if release in $(args)
+{
+ tbuildcmd = release ;
+}
+else if debug in $(args)
+{
+ tbuildcmd = debug ;
+}
+echo "tbuildcmd $(tbuildcmd)" ;
+echo "ARGV: $(args)" ;
+echo "INSTALLDIR/BINPREFIX: $(INSTALLDIR:E=not_set)$(BIN_PREFIX:E=not_set)" ;
+
include $(JAM_INCLUDE_PATH)/redemption-config.jam ;
include $(JAM_INCLUDE_PATH)/cxxflags.jam ;
include $(JAM_INCLUDE_PATH)/defines.jam ;
## -154,10 +175,22 ## alias install :
install-etc-ppocr
install-share
install-gettext
+ install-qtclient
;
alias exe : rdpproxy rdpclient rdpinichecker ;
alias libs : libredrec ;
+import notfile ;
+notfile post_qtclient : #my-post-command : libs exe ;
+actions my-post-command
+{
+ echo my-post-command $(tbuildcmd) runs.
+ cd projects/qtclient ;
+ bjam $(tbuildcmd) ;
+ cd ../.. ;
+ rsync -aP projects/qtclient/bin/gcc-9/$(tbuildcmd)/. bin/gcc-9/$(tbuildcmd)/
+}
+
alias ocr_tools : display_learning extract_text ppocr_extract_text ;
alias install-etc-ppocr : install-etc-ppocr-latin install-etc-ppocr-latin-cyrillic ;
## -199,6 +232,39 ## install install-lib
: <location>$(INSTALLDIR)$(LIB_PREFIX)
;
+import path : basename ;
+
+#epath = "$(REDEMPTION_ROOT_PATH)/projects/qtclient/bin/gcc-9/release/qt5client" ;
+#ename = basename ( $(epath) ) ; # nope, causes target to not be found :(
+# adds newline at end
+#ename = [ SHELL "basename $(epath)" ] ;
+#ename = SHELL "basename $(epath)" ; # does not work
+actions make_qtclient_install
+{
+ echo "make_qtclient_install: REDEMPTION_ROOT_PATH $(REDEMPTION_ROOT_PATH) PWD $(PWD:E=not_set)" ;
+ echo [ SHELL "pstree -s -p $$ && echo PID $$ PWD $PWD" ] ;
+ # unfortunately, when we get here, bjam is not called with debug or release, so tbuildcmd is empty
+ # so assuming we delete our directories manually:
+ # rm -rf bin/gcc-9/* projects/qtclient/bin/gcc-9/* debian/* ./bin/project-cache.jam ./projects/qtclient/bin/project-cache.jam
+ # we can count on only one qt5client being built in qtclient bin dir, so target it with wildcards
+ # also, debian/buildtmp/usr/local/bin/ $(INSTALLDIR)$(BIN_PREFIX)/ might not exist at this time
+ # so use install instead of cp
+ # don't use local here, crashes "not in a function"
+ # cannot get the stupid glob to work, write manually
+ #epath = [ glob "$(REDEMPTION_ROOT_PATH)/projects/qtclient/bin/gcc-9/*/qt*client" ] ;
+ #epath="$(REDEMPTION_ROOT_PATH)/projects/qtclient/bin/gcc-9/release/qt5client" ;
+ #ename = [ SHELL "basename $(epath)" ] ;
+ #ename=`basename $(epath)`
+ # don't bother with variables, nothing works here https://stackoverflow.com/questions/75342602/bjam-cannot-assign-a-literal-to-a-variable
+ #echo "epath $(epath) ename $(ename)" ;
+ #cp -av $(REDEMPTION_ROOT_PATH)/projects/qtclient/bin/gcc-9/*/qt*client $(INSTALLDIR)$(BIN_PREFIX)/ ;
+ # just type everything verbatim:
+ echo install -m 775 -D $(REDEMPTION_ROOT_PATH)/projects/qtclient/bin/gcc-9/release/qt5client $(INSTALLDIR)$(BIN_PREFIX)/qt5client ;
+ install -m 775 -D $(REDEMPTION_ROOT_PATH)/projects/qtclient/bin/gcc-9/release/qt5client $(INSTALLDIR)$(BIN_PREFIX)/qt5client ;
+}
+explicit install-qtclient ;
+make install-qtclient : : #make_qtclient_install ;
+
exe redrec
:
First, we'd like to know whether we're doing debug or release build; apparently in bjam this is a "variant" which is a type of "feature", and I couldn't figure out how to read it ( Getting the build type/variant inside a bjam Jamfile / echo a feature? ). So I made a workaround where command line arguments are used - of course, this is only good when you use a bjam command that explicitly includes debug or release on the command line.
Then, a notfile is used to define something that functions like a new, custom/manual target - in the sense that we can call post_qtclient and it will trigger the build of libs and exe as dependencies; and then the my-post-command action is ran. The commands in this action run in the shell, so we can change directory to the subfolder project, run bjam in debug or release (via variable), and once done, we copy all files form the subfolder project bin directory to the main bin directory.
This is good enough for a build via bjam -a release post_qtclient - but not good enough if you intend to run debhelper scripts to build a .deb package (done by a tool in this repository, ./tools/packager.py --build-package --force-build).
To do that, we must copy the resulting executable file of the subfolder into the right install location; that is done by the install-qtclient "make" action (or whatever that is). It is very tricky to handle string there (left comments to document that) - so in the end, this part does a copy of the hardcoded subfolder project executable into the "bin" install location, as for other executables - however, using install, since at the time this piece runs, $(INSTALLDIR)$(BIN_PREFIX) might not yet exist!
This generally takes care of the install part; and to finally make sure that the subproject executable does end up in the .deb file, change /packaging/template/debian/rules like so:
diff --git a/packaging/template/debian/rules b/packaging/template/debian/rules
index 014c470e4..dab9a70ea 100755
--- a/packaging/template/debian/rules
+++ b/packaging/template/debian/rules
## -32,7 +32,8 ## build:
# dh_testdir - test directory before building Debian package. It makes sure
# that debian/control exists also check Jamroot for redemption project.
dh_testdir Jamroot
- bjam -q $(BJAM_EXTRA_INSTALL) exe libs
+ #bjam -q $(BJAM_EXTRA_INSTALL) exe libs
+ bjam -q $(BJAM_EXTRA_INSTALL) post_qtclient
... and this should finally result with a build of a .deb file, that has the subproject executable in the same bin folder as the other executables that are products of this process.
Note that rebuilding the project from scratch might be tricky because of caching, so here I've done the below command to "clean everything":
rm -rf bin/gcc-9/* projects/qtclient/bin/gcc-9/* debian/* ./bin/project-cache.jam ./projects/qtclient/bin/project-cache.jam
Well, hope that was it for this problem ...
I am developing in XCode on Mac OS X El Capitan 10.11.4. My project mixes C, C++11 and Embedded Python.
My project works as I can invoke a Python script and return the data to C++ by embedding python. In my project I use absolute paths as pythons search path to load the script from 'C'.
XCode project/
-- Python.framework/
-- python/
---- mypython.py
-- python_interface.c
-- main.cpp
My questions are:
Q1: I have brew Python available but this isn't seen from XCode, instead the system supplied one is. This isn't a problem for now but I would eventually like to know how to point to my chosen installation.
I have bundled the brew Python.framework into my project so it links successfully. I know it doesn't invoke this because if I specify my module path incorrectly it complains the system python can not find it. Also, 'system(which python)' reports '/usr/bin/python'.
Q2: How do I specify relative search paths to python within XCode, i.e. to locate my local python module from 'C' code within my project?
Q3: How do I determine the absolute path of python 'requests' installation at runtime? My python module imports this and it could be different than what I specify.
Currently, I use Py_GetPath and Py_SetPath to indicate these using absolute paths, i.e. '/usr/local/lib/python2.7/site-packages' for 'requests'.
I know how to locate the module path within python itself (Find path of module without importing in Python) but this is not what I want to do. I need to know the path before my script is run.
As mentioned I am embedding python, so I am making the call from 'C' to my python script (see https://docs.python.org/2/extending/embedding.html?highlight=embedded#pure-embedding).
I have found the following links that show how to get the path of the executable. What I would like is the path of the project and use relative paths from that to locate my python module.
Relative Paths Not Working in Xcode C++
Programmatically retrieving the absolute path of an OS X command-line app
I have found this posting; Relative imports for the billionth time. My search path from 'C' code to my python script is relative but I believe this post is mainly about python scripts importing other modules relative to each other.
Q4: The result of adding or linking Frameworks to an XCode project is the same. In my case the Python.framework appears in the 'Project navigator' and it is added to the 'Link Binaries With Libraries' section. Yet the following two articles indicate that there is a difference between the two. It is not the size of the executable as I have tried both methods and this remains the same.
https://developer.apple.com/library/ios/recipes/xcode_help-structure_navigator/articles/Adding_a_Framework.html
This states;
"The frameworks you add this way are third-party-built bundles containing the framework’s object code and runtime resources. See related articles for information about linking to a framework without adding it to the project."
and contains a link to "Linking to a Library or Framework", whose url seems to contradict this (AddingaLibrarytoaTarget).
https://developer.apple.com/library/ios/recipes/xcode_help-project_editor/Articles/AddingaLibrarytoaTarget.html#//apple_ref/doc/uid/TP40010155-CH17
Thanks.
Some answers to my questions
I reorganised my project and created my own Makefile using various sources on google. This was because I wanted to port my project as I couldn't answer the above questions. I had worked with Makefiles some years before but I am relatively new to XCode.
To create my Makefile these are the resources I used;
https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html
C-library not linking using gcc/g++
https://www.daniweb.com/programming/software-development/threads/124637/makefile-for-c-one-file-programs
https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables
http://www.puxan.com/web/blog/HowTo-Write-Generic-Makefiles
https://www.gnu.org/software/make/manual/
http://nuclear.mutantstargoat.com/articles/make/#building-c-c-programs
https://www.gnu.org/software/make/manual/html_node/index.html
The next step is to automate the process of generating Makefiles;
http://www.ifnamemain.com/posts/2014/Mar/13/autoconf_automake/
New Makefile project structure
project/
-- Debug/ or Release/
-- Makefile
-- obj/
---- target .o files
-- python/
---- .py files
-- src/
---- C/C++ files incl headers
Makefile
# https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables
# $# Contains the target file name.
# $< Contains the first dependency file name.
# $^ The names of all the prerequisites, with spaces between them.
# Produces a Release build by default, or Debug build with ‘make debug’
EXEC = $(BUILD_DIR)/my_exe_name
CCFLAGS=-c -Wall
CXXFLAGS=-c -Wall -std=c++11
BINARY_DIR=Obj
BUILD_DIR=Release
PYTHON_CCFLAGS=$(shell python2.7-config --cflags)
PYTHON_LDFLAGS=$(shell python2.7-config --ldflags)
CPP_FILES := $(wildcard src/*.cpp)
C_FILES := $(wildcard src/*.c)
CPP_OBJ_FILES := $(addprefix $(BINARY_DIR)/,$(notdir $(CPP_FILES:.cpp=.o)))
C_OBJ_FILES := $(addprefix $(BINARY_DIR)/,$(notdir $(C_FILES:.c=.o)))
C_HEADERS = $(filter-out $(addsuffix .h, $(basename $(SOURCE_FILES))), $(wildcard src/*.h))
CPP_HEADERS = $(filter-out $(addsuffix .h, $(basename $(SOURCE_FILES))), $(wildcard src/*.hpp))
### Take the BUILD_DIR as a dependency but ignore it’s timestamp
### as it will change whenever something is written into it
$(EXEC): $(C_OBJ_FILES) $(CPP_OBJ_FILES) | ${BUILD_DIR}
#echo [ $(EXEC) ]
$(CXX) $(LD_FLAGS) $(PYTHON_LDFLAGS) -o $(EXEC) $^
### The objects in the BINARY_DIR folder require the BINARY_DIR to be created
### as well as any changes to the other dependencies
$(BINARY_DIR)/%.o: src/%.cpp $(CPP_HEADERS) $(C_HEADERS) | ${BINARY_DIR}
#echo [ make $# ]
$(CXX) $(CXXFLAGS) $(C11_FLAGS) -o $# $<
$(BINARY_DIR)/%.o: src/%.c $(C_HEADERS) | ${BINARY_DIR}
#echo [ make $# ]
$(CC) $(CCFLAGS) $(PYTHON_CCFLAGS) -o $# $<
${BINARY_DIR}:
mkdir $(BINARY_DIR)
$(BUILD_DIR):
mkdir $(BUILD_DIR)
### clean the targets. We can have either Debug or Release but I don’t know
### how to specify either in one line, i.e. we don’t want to do ‘make clean debug’
### to specify the Debug folder
.PHONY: clean cleanmsg cleanrel cleandeb
clean: cleanmsg cleanrel cleandeb
rm -r -f $(BINARY_DIR)
cleanmsg:
#echo Cleaning product and all .o files
cleanrel:
rm -r -f Release
cleandeb:
rm -r -f Debug
# Debug build
# Ensure these are last. I had them below the original decl of the flags
# and they were always set! Now you have to ‘make debug’ for it to take effect
.PHONY: debug
debug: CCFLAGS += -DDEBUG -g
debug: CXXFLAGS += -DDEBUG -g
debug: BUILD_DIR=Debug
debug: $(EXEC)
Using XCode with new Makefile project
To continue using XCode to develop my project I created an XCode project over my Makefile project. See http://hiltmon.com/blog/2015/08/01/simple-c-plus-plus-from-makefiles-to-xcode-builds/
Answers
By doing this I am able to somewhat answer the above questions. Q3 is still unanswered.
A1: Create a Makefile project and within it state the platform specified python installation. This way you do not need to bundle it into your XCode project.
See https://docs.python.org/2/extending/embedding.html?highlight=embedded#compiling-and-linking-under-unix-like-systems.
A2: Make the XCode project relative so your product resides in your project. Not the best answer if you want your exe installed in /usr/local/bin.
See How to change output directory for a target (gp_coder's answer).
My Makefile stores the exe in either ./Release/ (make), or ./Debug/ (make debug). So does XCode but with different leading directories that are several levels deep. To change this, simply specify the current dir '.' in XCode for the Targets "Project Settings>Advanced>Custom>Products" destination directory, this will then match the Makefile.
My python search path is set to
:../python:/usr/local/lib/python2.7/site-packages
'../' because my exe is in ./Debug or./Release and python/ is relative to that.
However, if you were to invoke the exe from the cmdline one dir up it wouldn't work. e.g. ./Release/my_exe_name. This is because it is taking the current dir and using the search path in relation to that.
It would be better to store and set the absolute path. See Where to store application data (non-user specific) on Linux
I posted a question on this before I realised the answer; How to set relative project path in XCode when target is /usr/local/bin
The second path is for my python requests directory. Q3 refers to this.
A3: Don't know. Still have to work out how to pass my installed python module path into my C code at runtime BEFORE invoking python, as this makes up my python search path. This value changes based on your installation.
A4: This wasn't really a question but a confusion. Specify this in your Makefile to avoid bundling.
I'd like a set of makefile rules that create a symlink to one of several code modules before building the project. The name of the make target would determine the file to which the symlink points. For example:
The user invokes 'make R3000'
Make sees that 'data.asm' doesn't exist yet, so a symlink is created from 'data_R3000.asm' to 'data.asm'
The build process continues, using data.asm
How can I set up make rules to do this?
Maybe something like:
MODULES := $(patsubst data_%.asm,%,$(wildcard data_*.asm))
all:
...
data.asm:
[ -n "$(filter $(MAKECMDGOALS),$(MODULES))" ] || { echo unknown module: $(MAKECMDGOALS) ; exit 1; }
ln -s $(filter $(MAKECMDGOALS),$(MODULES)) $#
Then make sure data.asm is listed as a prerequisite in the appropriate rules.
I would do something like this:
.PHONY mklink
mklink:
test -e data_$(MAKECMDGOALS).asm || exit 1
ln -s data_$(MAKECMDGOALS).asm data.asm
and then make all (and other targets) dependent on mklink. The reason you shouldn't make data.asm your target in the rule is that if you run make R3000, then data.asm will be created, and then if you run make L2000, the data.asm file will be pointing to the wrong directory, and will not be overwritten (I'll assuming this is not what you want). The test line checks if the link target exists, and if not, it exits with 1, causing the target to fail. You should also add a check that MAKECMDGOALS is exactly one element as well.
When I tried my makefile, I got error saying that No such file or directory, but my directory is right there, what do I do wrong? Thanks.
my project structure :
dev |--- ev
|--- display
|--- install ( makefile is here, try to call makefiles in ev and display folder)
My makefile :
MODULES :=ev display
SRC_DIR :=$(addprefix ../, $(MODULES))
BUILD_DIR:=$(addsuffix /build, $(SRC_DIR))
x:=../ev ------> add temporarily just for test,
------> the same error if x:=$(HOME)/dev/ev
INSTALL_DIR:=EX Frameworks Add-ons
INSTALL_DIR:=$(addprefix $(HOME)/EX/, $(INSTALL_DIR))
vpath %.cpp %.java $(SRC_DIR)
.PHONY: all clean
checkdirs: $(INSTALL_DIR)
$(INSTALL_DIR):
#echo "INSTALL DIR"
#mkdir -p $#
define make-goal
$1:
#echo "start building each part"
cd $# && make -f Makefile_new.osx clean
cd $# && make -f Makefile_new.osx package
endef
clean:
#echo "clean up"
#echo "BUILD_DIR IS $(BUILD_DIR)"
#rm -rf $(BUILD_DIR)
all:
#echo "start build subdirectory"
#echo "SRC_DIR IS $(SRC_DIR)"
#echo "x is $(x)"
$(call make-goal, $(x))) ----> when it comes to here, I got error message
The error messages:
x is ../ev
../x:
make: ../ev:: No such file or directory.
I guess it is about relative path, because I call this makefile from Install folder, then $(x) can't be found from Install folder, but when I tried to make a folder named ev (Install/ev), I still got the same error.
I think it must be something basic I missed here, but what it is.
Thanks.
Update:
I am trying to build a project which includes several sub-projects. the structure is:
dev |---- ev
|---- edf
|----- dt
|------af
|------Install
Inside of Install, I have a makefile, which is at the top level. The makefile in Install folder will call makefiles in other folders to build different subjects,
Ideally, I want to build every sub projects without touching sources. My sources include c++ and java code.
It's not clear what you're trying to do. Also due to some indentation hiccups I can't be sure, but you appear to be defining a variable make-goal that contains a template for a make rule, then using it with $(call ...) inside the recipe for the all target. That cannot work: you cannot create a make rule inside the recipe for another make rule!
The reason this fails is that the $(call ...) is expanding to content which is added to the recipe of the all target, so instead of creating a new make rule it's treating the result as a shell script. The first line is $1:, and you passed in ../ev, so make is trying to run the command ../ev: just as the error shows you.
If you describe what you want to do at a higher level we can give you some ideas on how to do it.
ETA:
If you just want your all target to also build a subdirectory, there's no need for all this complex GNU make advanced capabilities. That stuff is only needed when you get to guru-level makefile creation. Simple "build a target after another target is finished" is the exact thing make was designed to do: nothing special is needed to do that.
For example:
.PHONY: all $(SRC_DIR)
all: $(SRC_DIR)
$(SRC_DIR):
#echo "start building $#"
cd $# && $(MAKE) -f Makefile_new.osx clean
cd $# && $(MAKE) -f Makefile_new.osx package
This is still a pretty non-standard setup but I think it will work the way you want. Remember you'll have to either move the all target up to be the first one in the makefile, or you'll have to run make all explicitly: make only builds the first target in the makefile unless you give it specific targets on the command line.
It is clear that the target is newer than the source from these two ls
comands:
[metaperl#andLinux ~/edan/pkg/gist.el] ls -l ../../wares/gist.el/gist.elc #target
-rw-r--r-- 1 metaperl metaperl 10465 Jul 18 10:56 ../../wares/gist.el/gist.elc
[metaperl#andLinux ~/edan/pkg/gist.el] ls -l yank/gist.el/gist.el #source
-rw-r--r-- 1 metaperl metaperl 13025 Jul 18 10:57 yank/gist.el/gist.el
[metaperl#andLinux ~/edan/pkg/gist.el]
However when I run makepp -v I am told that this rule depends not only
on the listed target, but also on the cd and mv commands.
makepplog: Targets
/home/metaperl/edan/wares/gist.el/gist.elc'
depend on/usr/local/bin/emacs',
/home/metaperl/edan/pkg/gist.el/yank/gist.el/gist.el',
/bin/mv'
What aspect of make logic dictates that the actions to produce the
target are part of the dependency chain of deciding on whether to make
the target?
To my mind, only the listed sources should affect whether or not the
target is rebuilt.
The entire makepp -v output is quite long, and exists at:
http://gist.github.com/480468
My makepp file:
include main.makepp
#VER
PKG := gist.el
URL := http://github.com/defunkt/$(PKG).git
TARGET := $(WARES)gist.el/gist.elc
$(TARGET) : yank/gist.el/gist.el
cd $(dir $(input)) && $(BYTECOMPILE) gist.el
mv $(dir $(input)) $(WARES)
perl {{
print 'github username: ';
my $username = <STDIN>;
print 'github API token: ';
my $api_token = <STDIN>;
system "git config --global github.user $username";
system "git config --global github.token $api_token";
use File::Butler;
my $lines = Butler('init.el', 'read');
my $loc = sprintf '%s%s', $EDAN_PKG, "$PKG/";
$lines =~ s/__LOC__/$loc/g;
$lines =~ s/__PKG__/$PKG/g;
Butler( $EDAN_EL, prepend => \$lines );
}}
yank/gist.el/gist.el : yank
cd yank && git clone http://github.com/defunkt/gist.el.git
yank:
mkdir yank
$(phony clean):
$(RM) -rf $(dir $(TARGET)) yank
With a standard make, the contents of the commands to make a target are not taken into account when deciding whether to rebuild the target. Only the dependencies are taken into account; this can go beyond the source if you have dependencies declared elsewhere.
You don't show your makeppfile, so I can't be sure, but the Parsing command... messages from makepp -v make me suspect that makepp behaves differently from standard make on this count.
makepp will rebuild a target if any of the dependencies have changed or if the command has changes. In your case, I suspect that either some of the variables that you use in the rule to make $(TARGET) have changed or that makepp is seeing that the commands are constructed dynamically and is automatically rebuilding the target. Try using the -m target_newer option to makepp to force it to use the old GNU make method (that is, only re-build if the source is newer than the target).
Link