Hi.
In my previous project, all the makefiles are home made.
And dependencies are fully managed.
The makefile can be called with clean / dep / all
dep, will automatically generates some <filename>.dep
it calls gcc with some special options and generate (e.g):
obj/VLX_NoiseMgr.o : VLX_NoiseMgr.cpp ../../include/CSC_Utils/UTI_Error.h \
../../include/CSC_Utils/UTI_types.h \
/usr/local/include/wx-2.8/wx/wxprec.h \
/usr/local/include/wx-2.8/wx/defs.h \
/usr/local/include/wx-2.8/wx/platform.h \
one part of the makefile :
SrcSuf = .cpp
DepSuf = .dep
ObjSuf = .o
OBJDIR = obj
SRCS = $(SRC)
OBJF = $(patsubst %$(SrcSuf),$(OBJDIR)/%$(ObjSuf),$(SRCS))
DEPS = $(patsubst %$(SrcSuf),$(OBJDIR)/%$(DepSuf),$(SRCS))
DEBUGFLAGS = -Wall -g -O2 -D_NO_SHUTDOWN_
CFLAGS = -D_REENTRANT `$(WXCONFIG) --cxxflags`
INCFLAGS = \
-I../../include/CSC_Spectro \
all:: $(DEPS) $(OBJF)
ifeq ($(MAKECMDGOALS),all)
include $(DEPS)
endif
$(OBJDIR)/%$(DepSuf): %$(SrcSuf)
@echo
@echo '------------------'
@echo -en "\033[30;47;1m"
@echo 'Making $< dependencies...'
@echo -en "\033[30;47;0m\n"
@if [ ! -d $(OBJDIR) ] ; then mkdir $(OBJDIR) ;fi ;
@$(CC) $(DEBUGFLAGS) $(CFLAGS) $(INCFLAGS) $(PROJFLAGS) -MM $< | sed 's/\($*\)\.o[ :]*/$(@D)\/\1.o : /' > $@ ;
$(OBJDIR)/%$(ObjSuf): %$(SrcSuf)
@echo '------------------'
@echo -en "\033[30;47;1m"
@echo 'Building $< ...'
@$(CC) $(DEBUGFLAGS) $(CFLAGS) $(INCFLAGS) $(PROJFLAGS) -c $< -o $@ ;
@echo -en "\033[30;47;0m\n"
clean::
@rm -rf $(OBJDIR)
print::
@echo "Dependances"
@echo $(DEPS)
@echo "Fichiers"
@echo $(OBJF)
Something like the following might help you get the dependencies that CB already sets up.
//----------------------------------------------------------------------------
void <yourclassname>::WriteMakDependencies(wxFFile& makeFile_fp)
// ----------------------------------------------------------------------------
{
cbProject* prj = GetProjectManager()->GetActiveCBProject();
if (not prj) return;
wxFileName fn(GetProjectManager()->GetActiveCBProjectFilename());
fn.SetExt(_T("depend"));
wxString depsFilename = fn.GetFullPath();
wxArrayString depsarr;
wxString str;
// Get array containing lines like "filename;dependfile;dependfile;"
GetMakeDependencies( depsFilename, depsarr);
if (depsarr.GetCount() == 0)
return;
for (size_t knt=0; knt < depsarr.GetCount(); ++knt)
{
wxArrayString adepLine = wxStringTokenize(depsarr[knt], wxT(";"));
wxString shortFilename = adepLine[0].AfterLast(wxFILE_SEP_PATH);
wxString filenameBase = shortFilename.BeforeLast(_T('.'));
if ( not shortFilename.AfterLast(_T('.')).StartsWith(_T("c")) )
continue; //not a .cxx file
if (adepLine.GetCount() > 1) do
{ //source has dependencies
wxString depstr = wxEmptyString;
for (int j=1; j<(int)adepLine.GetCount(); ++j)
{ // append the dependecies;
adepLine[j].Replace(_T("<"),_T(""));
adepLine[j].Replace(_T(">"),_T(""));
depstr.Append( adepLine[j] + _T(" "));
}
str.Printf( _T("%s.o: %s\n"), filenameBase.c_str(), depstr.c_str() );
makeFile_fp.Write( CvtU2C(str), str.Length()); //name.o: dependent files
}while(0);
str.Printf( _T("%s.o: %s\n"), filenameBase.c_str(), shortFilename.c_str());
makeFile_fp.Write( CvtU2C(str), str.Length()); //name.o: cxx filename
str.Printf( _T("\t$(CC) -c $(CFLAGS) %s\n"), shortFilename.c_str() );
makeFile_fp.Write( CvtU2C(str), str.Length());
}
}//WriteMakDependencies
// ----------------------------------------------------------------------------
bool <yourclassname>::GetMakeDependencies(wxString& filepath, wxArrayString& depsarr )
// ----------------------------------------------------------------------------
{
// Read .depend file into a wxArrayString consisting of lines of
// sourcefilename;dependfilename;dependfilename;
// sourcefilename;
// sourcefilename;dependfilename; etc
if (not ::wxFileExists(filepath))
return false;
wxString str;
FILE *f;
char buf[1024];
//int vmajor, vminor;
wxString h;
int n;
time_t timeval;
depsarr.Clear();
wxFFile file(filepath, _T("r"));
if ( not file.IsOpened() )
return false;
f = file.fp();
/* Skip magic */
fgets(buf, sizeof(buf), f);
while (fgets(buf, sizeof (buf), f))
{
buf[strlen(buf) - 1] = '\0'; /* zap newline */
if (!buf[0])
continue;
if (buf[0] == '\t')
{
str.Append( CvtC2U(buf+1) +_T(";"));
continue;
}
sscanf(buf, "%ld %n", &timeval, &n);
if ( not str.empty())
{
depsarr.Add(str);
}
str = CvtC2U(buf+n) + _T(";");
}
// last buffer
if ( not str.empty())
{
depsarr.Add(str);
}
//fclose(f); file closed by wxFFile
return true;
}
a remark with respect to casing :
I have a cbp project, with the following targets : Debug and Release
However in the created make file, all is talking about 'debug' and 'release'. I would expect the same casing is used :
Eg :
debug: before_debug out_debug after_debug
would become (and all other likewise occurrences ) :
Debug: before_Debug out_Debug after_Debug
What do you think ?
There's something fishy going on with global variables.
I've got these in the global configuration:
<globalvariables>
<variableset name="default">
<variable name="qt4" description="">
<builtin base="/home/oskari/qt-4.7.4/" include="" lib="" obj="" cflags="" lflags="" />
<user />
</variable>
<variable name="trak" description="">
<builtin base="/home/oskari/TCF" include="" lib="" obj="" cflags="" lflags="" />
<user />
</variable>
</variableset>
</globalvariables>
And below is the stuff generated in the top of the makefile:
qt4 = /home/oskari/qt-4.7.4/
qt4_include = /home/oskari/qt-4.7.4//include
qt4_lib = /home/oskari/qt-4.7.4//lib
qt4_obj =
qt4_cflags =
qt4_lflags =
trak = /home/oskari/TCF
trak_include = /home/oskari/TCF/include
trak_lib = /home/oskari/TCF/lib
trak_obj =
trak_cflags =
trak_lflags =
All the variables are in small letters in my original .cbp file, but still theres some places where the variables are either not replaced or they are in all caps:
INC = -I$(TRAK)/ClientFramework/include -I$(TRAK)/ClientFramework/lib/libstrophe -I$(TRAK)/ClientFramework/lib/qxmpp/src -I$(QT4_INCLUDE) -I$(QT4_INCLUDE)/QtCore -I$(QT4_INCLUDE)/QtNetwork -I$(QT4_INCLUDE)/QtXml
LIBDIR = -L$(TRAK)/build/lib/$(TARGET_NAME)/ -L$(TRAK)/ClientFramework/lib/qxmpp/lib -L$(QT4_LIB)
OUT_DEBUG = $(#trak)/build/bin/$(TARGET_NAME)/TCF.so
...
before_Debug:
test -d $(#trak)/build/bin/$(TARGET_NAME) || mkdir -p $(#trak)/build/bin/$(TARGET_NAME)
...
This causes problems at least for me, because make seems to be case-sensitive when it comes to variables. Also, would it be possible to define the $(PROJECT_NAME) and $(TARGET_NAME)? For example, I have the following in my .cbp:
<Target title="Debug">
<Option output="$(#trak)\build\bin\$(TARGET_NAME)\TCF" prefix_auto="1" extension_auto="1" />
<Option object_output="$(#trak)\build\obj\$(TARGET_NAME)\$(PROJECT_NAME)\" />
...
Hello!
This tool is exactly what I was searching for. I am in Windows and I am trying to create a makefile from my .cbp project. The problem is that when I try to do that cbp2make crushes immediately.
I am typing in:
The output I get is:
Starting cbp2make rev.133...
Using default configuration.
Loading file 'foo.cbp': [DONE]
Generating makefile(s): foo.cbp.mak:
After that the crush happens.
Any idea why this might be happening or methods to correct it?
My project is actually cross-platform thanks to CB so, I am gonna go to my Linux pc and try to run cbp2make in the same project from there and see the results.
I tested it in Linux too and the same thing happened. So it must be something inside the .cbp file
I have different build targets in the .cbp project. Should not affect the generation process though right?
Further Edit:
Since I had the source I thought I should try to debug it and maybe see if the seg fault is something easy to fix. After lots of trial and error and updates on this post I found what was happening.
The problem is in line 1098 of src\cbproject.cpp
CBuildUnit *unit = m_UnitIndex[i];
CCompiler *compiler = tc->FindCompiler(unit->Extension());
/*
std::cout<<"target "<<target->Name().GetString()
<<" unit "<<unit->m_FileName.GetString()
<<" compilable "<<unit->IsCompilable()
<<" compile "<<unit->m_DoCompile
<<" link "<<unit->m_DoLink<<std::endl;
*/
if( compiler !=0) /// Extra check added by Lefteris, so as not to delete the original check
{
//if (((0!=compiler)||(!unit->CompilerVariable().IsEmpty()))&&(unit->DoCompile()))//original check
CString compiler_var = compiler->MakeVariable(); /// SEG FAULT HERE
if (unit->CompilerVariable().IsEmpty())
{
compiler_var = compiler->MakeVariable();
}
The original if check allows the program to get in the compilable unit scope even if the CCompiler* pointers is 0. So for all the header files of my project it was getting in there and was causing a seg fault.
I made a temporary fix of this if check by just checking only for compiler pointer being 0.
Further Edit 2
I discovered another bug. In windows during the linking stage the loading flags were output to the
beginning right after the $(LD). That resulted in undefined referenced during making.
out_win32_debug_test: $(OBJ_WIN32_DEBUG_TEST) $(DEP_WIN32_DEBUG_TEST)
$(LD) $(LDFLAGS_WIN32_DEBUG_TEST) $(LIBDIR_WIN32_DEBUG_TEST) $(OBJ_WIN32_DEBUG_TEST) $(LIB_WIN32_DEBUG_TEST) -o $(OUT_WIN32_DEBUG_TEST)
I am not sure if this is an issue of cbp2make or if it is supposed to be okay to put the Loader Flags wherever you want, but moving them to the very end like that solved my problem.
out_win32_debug_test: $(OBJ_WIN32_DEBUG_TEST) $(DEP_WIN32_DEBUG_TEST)
$(LD) $(LIBDIR_WIN32_DEBUG_TEST) $(OBJ_WIN32_DEBUG_TEST) $(LIB_WIN32_DEBUG_TEST) -o $(OUT_WIN32_DEBUG_TEST) $(LDFLAGS_WIN32_DEBUG_TEST)
fruitCode
Compiler settings from C::B are not transferred to cbp2make.cfg automatically, you have to specify options separately from C::B.
Run cbp2make --config --local , this will create cbp2make.cfg file in the current directory. Then you can edit this XML file either with a regular text editor or by running cbp2make with certain options. I think using a text editor would be easier.
Find a section like this
<toolchain platform="Windows" alias="gcc">
<tool type="compiler" alias="gnu_c_compiler">
<option description="GNU C Compiler" />
<option program="gcc.exe" />
<option make_variable="CC" />
<option command_template="$compiler $options $includes -c $file -o $object" />
<option source_extensions="c cc" />
<option target_extension="o" />
<option need_quoted_path="0" />
<option need_full_path="0" />
<option need_unix_path="0" />
<option include_dir_switch="-I" />
<option define_switch="-D" />
</tool>
<tool type="compiler" alias="gnu_cpp_compiler">
<option description="GNU C++ Compiler" />
<option program="g++.exe" />
<option make_variable="CXX" />
<option command_template="$compiler $options $includes -c $file -o $object" />
<option source_extensions="cpp cxx" />
<option target_extension="o" />
<option need_quoted_path="0" />
<option need_full_path="0" />
<option need_unix_path="0" />
<option include_dir_switch="-I" />
<option define_switch="-D" />
</tool>
<tool type="static_library_linker" alias="gnu_static_linker">
<option description="GNU Static Library Linker" />
<option program="ar.exe" />
<option make_variable="AR" />
<option command_template="$lib_linker rcs $static_output $link_objects" />
<option source_extensions="o obj" />
<option target_extension="a" />
<option need_quoted_path="0" />
<option need_full_path="0" />
<option need_unix_path="0" />
<option library_dir_switch="-L" />
<option link_library_switch="-l" />
<option object_extension="o" />
<option library_prefix="lib" />
<option library_extension="a" />
<option need_library_prefix="0" />
<option need_library_extension="0" />
<option need_flat_objects="0" />
</tool>
<tool type="dynamic_library_linker" alias="gnu_dynamic_linker">
<option description="GNU Dynamic Library Linker" />
<option program="g++.exe" />
<option make_variable="LD" />
<option command_template="$linker -shared $link_options $libdirs $link_objects $libs -o $exe_output" />
<option source_extensions="o obj" />
<option target_extension="dll" />
<option need_quoted_path="0" />
<option need_full_path="0" />
<option need_unix_path="0" />
<option library_dir_switch="-L" />
<option link_library_switch="-l" />
<option object_extension="o" />
<option library_prefix="lib" />
<option library_extension="dll" />
<option need_library_prefix="0" />
<option need_library_extension="0" />
<option need_flat_objects="0" />
</tool>
<tool type="executable_binary_linker" alias="gnu_executable_linker">
<option description="GNU Executable Binary Linker" />
<option program="g++.exe" />
<option make_variable="LD" />
<option command_template="$linker $link_options $libdirs $link_objects $libs -o $exe_output" />
<option source_extensions="o obj" />
<option target_extension="exe" />
<option need_quoted_path="0" />
<option need_full_path="0" />
<option need_unix_path="0" />
<option library_dir_switch="-L" />
<option link_library_switch="-l" />
<option object_extension="o" />
<option library_prefix="" />
<option library_extension="" />
<option need_library_prefix="0" />
<option need_library_extension="0" />
<option need_flat_objects="0" />
<option option_wingui="-mwindows" />
</tool>
<tool type="resource_compiler" alias="gnu_windres_compiler">
<option description="GNU Windows Resource Compiler" />
<option program="windres.exe" />
<option make_variable="WINDRES" />
<option command_template="$rescomp -i $file -J rc -o $resource_output -O coff $includes" />
<option source_extensions="rc res coff" />
<option target_extension="o" />
<option need_quoted_path="0" />
<option need_full_path="0" />
<option need_unix_path="0" />
<option include_dir_switch="-I" />
<option define_switch="-D" />
</tool>
</toolchain>
Copy this text next to itself and replace the value of "alias" option with exactly the same text as in .cbp file in line "<Option compiler = "gcc">", then modify options of build tools to match C::B settings.
What's the current state of this project?
Any update on variable support? I know that C::B variables are a mess in themselves.. (that is, no documentation, no preview function etc... you'll have to guess and Google how to use them^^ The IDE doesn't help you at all. Besides the "tools" that use their own variables... though often redundant as there exist C::B equivalents..)
- Would be nice if I could just use those post-build steps:
objcopy --only-keep-debug $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).dbg
objcopy --strip-debug --strip-unneeded --add-gnu-debuglink=$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME).dbg $(TARGET_OUTPUT_FILE)
- The current version fails to create virtual targets... basically it only ever creates the first virtual target. (which is one reasons I can't use it)
- Targets cannot share object files... at least not without warnings such as:
Makefile:457: warning: overriding recipe for target '.obj\\common\\clock.o'
Makefile:383: warning: ignoring old recipe for target '.obj\\common\\clock.o'
383: $(OBJDIR_CALENDAR)\\common\\clock.o: common\\clock.c
$(CC0) $(CFLAGS_CALENDAR) $(INC_CALENDAR) -c common\\clock.c -o $(OBJDIR_CALENDAR)\\common\\clock.o
457: $(OBJDIR_APP)\\common\\clock.o: common\\clock.c
$(CC0) $(CFLAGS_APP) $(INC_APP) -c common\\clock.c -o $(OBJDIR_APP)\\common\\clock.o
- I would also love if virtual targets wouldn't be prefixed "virtual_". I guess no one wants to "make virtual_debug" when it could be "make debug" or "make release" or even "make all" (that is, allow a virtual "all" target to overwrite default "all")
- cbp2make doesn't properly append target options to project global ones.. It creates code like this: "$(LIB0)-lComctl32", notice the missing space.
- clean fails if a directory or file was already "cleaned" and no longer exists... it doesn't properly handle shared object as well, because it tries to remove the object directory which might still contain objects..
And about Sourceforge... why not just move on to GitHub or GitLab ;)
addition 06/11/2015:
just noticed that
- "use target options only" doesn't always work.. seems to work for compiler options and some linker options, but not for link libraries
- "target_extension"/"library_extension" config option is totally ignored. At least for "executable_binary_linker" and "dynamic_library_linker" because cbp2make only ever uses AutoFileExtension() which doesn't use "target_extension" at all and returns the target OS' default
addition 06/16/2015:
- "option_wingui" should work for any platform. Currently it's wrapped around an if ((pl->OS()==CPlatform::OS_Windows) && (target->Type()==CBuildTarget::ttExecutable))
But the platform check is redundant as "option_wingui" is empty on any non-Windows platform... and if it's not, there's a high chance that it is required. So don't limit options by platform, let options "limit" themselves. (this basically prevents proper cross-platform builds / makefile generation)
- if make is called with -j 4 to use parallel-builds, post-build steps might fail because they don't depend on the base build step (out_*)
though, it seems like cbp2make knew about this issue when it created "out_*" steps as they depend properly on "before_*" (pre-build)
- Targets cannot share object files... at least not without warnings such as:
They shouldn't share them. I personally consider this a poorly configured project.
To enable shared object files between targets one should at first verify that the build options for these files are effectively equal between targets.
Otherwise you're messing up your project by mixing in possibly incompatible object files.
actually a reason why targets should be able to share them.
One project usually got the very same compiler options, if you're forced to split it into different projects so that make files are properly generated, it's way more likely to have incompatible compiler flags...
And sharing the very same source files shouldn't be a problem as well... In case you're compiling multiple programs that share some common code. (ok, one could use different object directories...)
- cbp2make doesn't properly append target options to project global ones.. It creates code like this: "$(LIB0)-lComctl32", notice the missing space.
There were such issues before, but they should be already fixed by now. Does this bug still occur?
I'm always using the latest versions before I report stuff.. besides I'm generally always using (and checking for) the latest version before I use a program.
And your latest release is rev147 which is 2 years old.
- clean fails if a directory or file was already "cleaned" and no longer exists... it doesn't properly handle shared object as well, because it tries to remove the object directory which might still contain objects..
This follows exactly from the same rule that object files must not be shared between targets.
Untrue, this issue doesn't exist on Linux. *nix generated makefiles use "if"'s before trying to delete files. Windows does not.
This basically means that you can't "make clean" twice, or can't use it on a clean checkout of a repository.. (just to make sure everything is cleaned)
It shouldn't fail though. Trying to delete a file that doesn't exist, still means that the file is successfully deleted / non-existent and the result is the expected clean state
- "option_wingui" should work for any platform. Currently it's wrapped around an if ((pl->OS()==CPlatform::OS_Windows) && (target->Type()==CBuildTarget::ttExecutable))
But the platform check is redundant as "option_wingui" is empty on any non-Windows platform... and if it's not, there's a high chance that it is required. So don't limit options by platform, let options "limit" themselves. (this basically prevents proper cross-platform builds / makefile generation)
AFAIR I considered that this kind of cross-platform builds is next to nonexistent.
Do you build Windows applications on Linux/Mac/... with native tools and foreign libraries?
Not sure what you mean exactly...
I'm using MinGW on Linux by installing "binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686".. Yet MinGW still requires the right switch to generate GUI applications for Windows... otherwise my cross-compiled programs will still show a console when run on Windows.
-- For testing:
You could use the project I'm using: https://github.com/White-Tiger/T-Clock.git
cd ./Source
# Make a copy of the manually fixed "Makefile.unix"
cbp2make --wrap-objects --keep-outdir --local -in T-Clock-gcc.cbp -out Makefile.unix -unix
and compare these... (ignore the -Os option, had to remove that to make it build on Ubuntu)