Author Topic: Patch to allow specification of the execution directory for custom makefiles  (Read 18366 times)

Offline gryphon

  • Multiple posting newcomer
  • *
  • Posts: 15
I've actually added this as a patch to berlios,
https://developer.berlios.de/patch/index.php?func=detailpatch&patch_id=2558&group_id=5358

but thought it would make sense to post here also as I think the opportunity for dialogue is probably greater in the forum.

In summary this patch adds the ability to specify an execution directory for a custom makefile. This makes it possible to use CodeBlocks for some projects which require this (for example this is needed where I currently work). Interestingly I see NetBeans actually provides this functionality also but NetBeans just isn't CodeBlocks so I thought I'd create a patch so I can use my favourite IDE  :wink:

Description

For some projects that make use of a custom makefile it is required that the execution directory of the makefile be specified. Paths within the makefile are relative to this execution directory and so will not be found.

For example given the following locations:

base/build/aaaa/source/sub_source/file.cpp
base/build/aaaa/source/Makefile
base/cbs/project/project.cbp


The makefile references files as:

./source/sub_source/file.cpp

The patch basically allows specification of an execution directory for the makefile other than the default of:

base/cbs/project/

In this case the required execution working directory would be:

../../build/aaaa

The patch enables four things:

1. It sets the correct directory for the piped process that runs make to the execution directory specified.

2. It changes the logged compile error and warning file paths into valid paths by combining the makefile execution directory and the invalid relative path taken from the makefile. This ensures that clicking on such error and warning messages results in the correct line in the correct file being focused as usual.

3. If no makefile command is specified in a target, but one is specified in the project then the project command is used.

4. Codeblocks variables (including target custom variables) are replaced in the makefile command so that targets can override behaviour of a single project level makefile command. This allows much easier use of custom makefiles.

For example, I can now have a makefile command for building like this at the project level:

make -f ./source/Makefile PRODUCT=$(PRODUCT) MIDDLEWARE=$(MIDDLEWARE)


Then in a target I can specify:

PRODUCT = product1
MIDDLEWARE = some_middleware

« Last Edit: September 07, 2008, 05:47:58 pm by gryphon »

Offline gryphon

  • Multiple posting newcomer
  • *
  • Posts: 15
The patch

Comments and corrections welcome  :)

Code
Index: src/sdk/cbproject.cpp
===================================================================
--- src/sdk/cbproject.cpp (revision 5201)
+++ src/sdk/cbproject.cpp (working copy)
@@ -1550,6 +1550,25 @@
     return m_Makefile;
 }
 
+void cbProject::SetMakefileExecutionDir(const wxString& dir)
+{
+    if (m_MakefileExecutionDir != dir)
+    {
+        m_MakefileExecutionDir = dir;
+        SetModified(true);
+    }
+}
+
+const wxString& cbProject::GetMakefileExecutionDir() const
+{
+    if (!m_MakefileExecutionDir.IsEmpty())
+        return m_MakefileExecutionDir;
+
+    wxFileName makefile_execution_dir(GetBasePath());
+    m_MakefileExecutionDir = makefile_execution_dir.GetFullPath();
+    return m_MakefileExecutionDir;
+}
+
 ProjectFile* cbProject::GetFile(int index)
 {
     FilesList::Node* node = m_Files.Item(index);
Index: src/sdk/projectoptionsdlg.cpp
===================================================================
--- src/sdk/projectoptionsdlg.cpp (revision 5201)
+++ src/sdk/projectoptionsdlg.cpp (working copy)
@@ -68,6 +68,7 @@
     EVT_BUTTON(    XRCID("btnBrowseOutputFilename"),   ProjectOptionsDlg::OnBrowseOutputFilenameClick)
     EVT_BUTTON(    XRCID("btnBrowseWorkingDir"),       ProjectOptionsDlg::OnBrowseDirClick)
     EVT_BUTTON(    XRCID("btnBrowseObjectDir"),        ProjectOptionsDlg::OnBrowseDirClick)
+    EVT_BUTTON(    XRCID("btnMakefileExecutionDir"),   ProjectOptionsDlg::OnBrowseDirClick)
     EVT_BUTTON(    XRCID("btnVirtualBuildTargets"),    ProjectOptionsDlg::OnVirtualTargets)
     EVT_BUTTON(    XRCID("btnExternalDeps"),           ProjectOptionsDlg::OnEditDepsClick)
     EVT_BUTTON(    XRCID("btnExportTarget"),           ProjectOptionsDlg::OnExportTargetClick)
@@ -110,6 +111,7 @@
     XRCCTRL(*this, "txtPlatformProj", wxTextCtrl)->SetValue(GetStringFromPlatforms(m_Project->GetPlatforms()));
     XRCCTRL(*this, "txtProjectMakefile", wxTextCtrl)->SetValue(m_Project->GetMakefile());
     XRCCTRL(*this, "chkCustomMakefile", wxCheckBox)->SetValue(m_Project->IsMakefileCustom());
+    XRCCTRL(*this, "txtMakefileExecutionDir", wxTextCtrl)->SetValue(m_Project->GetMakefileExecutionDir());
     XRCCTRL(*this, "rbPCHStrategy", wxRadioBox)->SetSelection((int)m_Project->GetModeForPCH());
 
     Compiler* compiler = CompilerFactory::GetCompiler(project->GetCompilerID());
@@ -687,6 +689,8 @@
         targettext = XRCCTRL(*this, "txtWorkingDir", wxTextCtrl);
     else if (event.GetId() == XRCID("btnBrowseObjectDir"))
         targettext = XRCCTRL(*this, "txtObjectDir", wxTextCtrl);
+    else if (event.GetId() == XRCID("btnMakefileExecutionDir"))
+        targettext = XRCCTRL(*this, "txtMakefileExecutionDir", wxTextCtrl);
     else
         return;
 
@@ -1021,6 +1025,10 @@
     XRCCTRL(*this, "btnToggleCheckmarks", wxButton)->Enable(!customMake && en);
     list->Enable(!customMake);
 
+    // enable some stuff if using a custom makefile
+    XRCCTRL(*this, "txtMakefileExecutionDir", wxTextCtrl)->Enable(customMake);
+    XRCCTRL(*this, "btnMakefileExecutionDir", wxTextCtrl)->Enable(customMake);
+
     // scripts page
     wxTreeCtrl* tc = XRCCTRL(*this, "tcOverview", wxTreeCtrl);
     tc->Enable(!customMake);
@@ -1055,6 +1063,7 @@
         m_Project->RenameInTree(m_Project->GetTitle());
         m_Project->SetMakefile(XRCCTRL(*this, "txtProjectMakefile", wxTextCtrl)->GetValue());
         m_Project->SetMakefileCustom(XRCCTRL(*this, "chkCustomMakefile", wxCheckBox)->GetValue());
+        m_Project->SetMakefileExecutionDir(XRCCTRL(*this, "txtMakefileExecutionDir", wxTextCtrl)->GetValue());
         m_Project->SetTargetType(TargetType(XRCCTRL(*this, "cmbProjectType", wxComboBox)->GetSelection()));
         m_Project->SetModeForPCH((PCHMode)XRCCTRL(*this, "rbPCHStrategy", wxRadioBox)->GetSelection());
         m_Project->SetExtendedObjectNamesGeneration(XRCCTRL(*this, "chkExtendedObjNames", wxCheckBox)->GetValue());
Index: src/sdk/projectloader.cpp
===================================================================
--- src/sdk/projectloader.cpp (revision 5201)
+++ src/sdk/projectloader.cpp (working copy)
@@ -377,6 +377,7 @@
     wxString title;
     wxString makefile;
     bool makefile_custom = false;
+    wxString makefile_execution_dir;
     wxString defaultTarget;
     wxString compilerId = _T("gcc");
     bool extendedObjectNames = false;
@@ -405,6 +406,9 @@
         else if (node->Attribute("makefile_is_custom"))
             makefile_custom = strncmp(node->Attribute("makefile_is_custom"), "1", 1) == 0;
 
+        else if (node->Attribute("makefile_execution_dir"))
+            makefile_execution_dir = cbC2U(node->Attribute("makefile_execution_dir"));
+
         // old default_target (int) node
         else if (node->QueryIntAttribute("default_target", &m_1_4_to_1_5_deftarget) == TIXML_SUCCESS)
         {
@@ -442,6 +446,7 @@
     m_pProject->SetPlatforms(platformsFinal);
     m_pProject->SetMakefile(makefile);
     m_pProject->SetMakefileCustom(makefile_custom);
+    m_pProject->SetMakefileExecutionDir(makefile_execution_dir);
     m_pProject->SetDefaultExecuteTarget(defaultTarget);
     m_pProject->SetCompilerID(compilerId);
     m_pProject->SetExtendedObjectNamesGeneration(extendedObjectNames);
@@ -1070,6 +1075,8 @@
         AddElement(prjnode, "Option", "makefile", m_pProject->GetMakefile());
     if (m_pProject->IsMakefileCustom())
         AddElement(prjnode, "Option", "makefile_is_custom", 1);
+    if (m_pProject->GetMakefileExecutionDir() != m_pProject->GetBasePath())
+        AddElement(prjnode, "Option", "makefile_execution_dir", m_pProject->GetMakefileExecutionDir());
     if (m_pProject->GetModeForPCH() != pchObjectDir)
         AddElement(prjnode, "Option", "pch_mode", (int)m_pProject->GetModeForPCH());
     if (!m_pProject->GetDefaultExecuteTarget().IsEmpty() && m_pProject->GetDefaultExecuteTarget() != m_pProject->GetFirstValidBuildTargetName())
Index: src/sdk/resources/project_options.xrc
===================================================================
--- src/sdk/resources/project_options.xrc (revision 5201)
+++ src/sdk/resources/project_options.xrc (working copy)
@@ -100,9 +100,40 @@
  <flag>wxLEFT|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
  <border>4</border>
  </object>
+ <object class="sizeritem">
+ <object class="wxBoxSizer">
+ <object class="sizeritem">
+ <object class="wxStaticText" name="ID_STATICTEXT19">
+ <label>Execution directory:</label>
+ </object>
+ <flag>wxLEFT|wxRIGHT|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+ <border>2</border>
+ </object>
+ <object class="sizeritem">
+ <object class="wxTextCtrl" name="txtMakefileExecutionDir">
+ <size>402,25</size>
+ <enabled>0</enabled>
+ </object>
+ <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+ <border>2</border>
+ <option>1</option>
+ </object>
+ <object class="sizeritem">
+ <object class="wxButton" name="btnMakefileExecutionDir">
+ <label>...</label>
+ <size>24,24</size>
+ <enabled>0</enabled>
+ </object>
+ <flag>wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
+ </object>
+ </object>
+ <flag>wxEXPAND|wxALIGN_LEFT|wxALIGN_TOP</flag>
+ <border>5</border>
+ <option>1</option>
+ </object>
  </object>
- <flag>wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL</flag>
- <border>5</border>
+ <flag>wxEXPAND|wxALIGN_LEFT|wxALIGN_TOP</flag>
+ <border>2</border>
  <option>1</option>
  </object>
  <object class="sizeritem">
Index: src/plugins/compilergcc/compiler_defs.h
===================================================================
--- src/plugins/compilergcc/compiler_defs.h (revision 5201)
+++ src/plugins/compilergcc/compiler_defs.h (working copy)
@@ -14,11 +14,11 @@
 
 struct CompilerCommand
 {
-    CompilerCommand(const wxString& cmd, const wxString& msg, cbProject* prj, ProjectBuildTarget* tgt, bool is_run = false)
-        : command(cmd), message(msg), project(prj), target(tgt), isRun(is_run), mustWait(false), isLink(false)
+    CompilerCommand(const wxString& cmd, const wxString& msg, const wxString& directory, cbProject* prj, ProjectBuildTarget* tgt, bool is_run = false)
+        : command(cmd), message(msg), dir(directory), project(prj), target(tgt), isRun(is_run), mustWait(false), isLink(false)
     {}
     CompilerCommand(const CompilerCommand& rhs)
-        : command(rhs.command), message(rhs.message), project(rhs.project), target(rhs.target), isRun(rhs.isRun), mustWait(rhs.mustWait), isLink(rhs.isLink)
+        : command(rhs.command), message(rhs.message), dir(rhs.dir), project(rhs.project), target(rhs.target), isRun(rhs.isRun), mustWait(rhs.mustWait), isLink(rhs.isLink)
     {}
     wxString command;
     wxString message;
Index: src/plugins/compilergcc/compilergcc.cpp
===================================================================
--- src/plugins/compilergcc/compilergcc.cpp (revision 5201)
+++ src/plugins/compilergcc/compilergcc.cpp (working copy)
@@ -1013,7 +1013,7 @@
         if (cmd.StartsWith(mySimpleLog))
         {
             cmd.Remove(0, mySimpleLog.Length());
-            m_CommandQueue.Add(new CompilerCommand(wxEmptyString, cmd, m_pBuildingProject, bt));
+            m_CommandQueue.Add(new CompilerCommand(wxEmptyString, cmd, wxEmptyString, m_pBuildingProject, bt));
         }
         // compiler change
         else if (cmd.StartsWith(myTargetChange))
@@ -1030,7 +1030,7 @@
         else
         {
             // compiler command
-            CompilerCommand* p = new CompilerCommand(cmd, wxEmptyString, m_pBuildingProject, bt);
+            CompilerCommand* p = new CompilerCommand(cmd, wxEmptyString, wxEmptyString, m_pBuildingProject, bt);
             p->mustWait = mustWait;
             p->isLink = isLink;
             m_CommandQueue.Add(p);
@@ -1669,7 +1669,7 @@
 
     Manager::Get()->GetMacrosManager()->ReplaceEnvVars(m_CdRun);
     Manager::Get()->GetLogManager()->Log(F(_("Executing: %s (in %s)"), cmd.c_str(), m_CdRun.c_str()), m_PageIndex);
-    m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, 0, 0, true));
+    m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, wxEmptyString, 0, 0, true));
     return 0;
 }
 
@@ -1862,7 +1862,7 @@
     }
 
     Manager::Get()->GetLogManager()->Log(F(_("Executing: %s (in %s)"), cmd.c_str(), m_CdRun.c_str()), m_PageIndex);
-    m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, m_Project, target, true));
+    m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, wxEmptyString, m_Project, target, true));
 
     m_Project->SetCurrentlyCompilingTarget(0);
     return 0;
@@ -1897,11 +1897,14 @@
     wxString compilerId = target ? target->GetCompilerID() : project->GetCompilerID();
     if (!CompilerFactory::IsValidCompilerID(compilerId))
         compilerId = CompilerFactory::GetDefaultCompilerID();
-    wxString command = target ? target->GetMakeCommandFor(cmd) : project->GetMakeCommandFor(cmd);
+    wxString command = target && !target->GetMakeCommandFor(cmd).empty() ?
+                       target->GetMakeCommandFor(cmd) : project->GetMakeCommandFor(cmd);
 
     command.Replace(_T("$makefile"), project->GetMakefile());
     command.Replace(_T("$make"), CompilerFactory::GetCompiler(compilerId)->GetPrograms().MAKE);
     command.Replace(_T("$target"), target ? target->GetTitle() : _T(""));
+    Manager::Get()->GetMacrosManager()->ReplaceMacros(command);
+
 //    Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("Make: %s"), command.c_str()));
     return command;
 }
@@ -1954,14 +1957,13 @@
     while (!m_BuildJobTargetsList.empty())
     {
         BuildJobTarget bjt = GetNextJob();
-        wxSetWorkingDirectory(bjt.project->GetBasePath());
         ProjectBuildTarget* bt = bjt.project->GetBuildTarget(bjt.targetName);
         CompilerFactory::GetCompiler(bt->GetCompilerID())->Init(bjt.project);
 
         if (UseMake())
         {
             wxString cmd = GetMakeCommandFor(mcClean, bjt.project, bt);
-            m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, bjt.project, bt));
+            m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, bjt.project->GetMakefileExecutionDir(), bjt.project, bt));
             return DoRunQueue();
         }
         else
@@ -2008,7 +2010,7 @@
     if (UseMake(target))
     {
         wxString cmd = GetMakeCommandFor(mcDistClean, m_Project, target);
-        m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, m_Project, target));
+        m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, m_Project->GetMakefileExecutionDir(), m_Project, target));
         return DoRunQueue();
     }
     else
@@ -2438,7 +2440,7 @@
     if (UseMake())
     {
         wxString cmd = GetMakeCommandFor(mcBuild, bj.project, bt);
-        m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, bj.project, bt));
+        m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, bj.project->GetMakefileExecutionDir(), bj.project, bt));
     }
     else
     {
@@ -2552,7 +2554,7 @@
             if (bt)
             {
                 wxString cmd = GetMakeCommandFor(mcBuild, bjt.project, bt);
-                m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, bjt.project, bt));
+                m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, bjt.project->GetMakefileExecutionDir(), bjt.project, bt));
             }
         }
     }
@@ -2620,11 +2622,11 @@
             if (bt)
             {
                 cmd = GetMakeCommandFor(mcClean, bjt.project, bt);
-                cc = new CompilerCommand(cmd, wxEmptyString, bjt.project, bt);
+                cc = new CompilerCommand(cmd, wxEmptyString, bjt.project->GetMakefileExecutionDir(), bjt.project, bt);
                 m_CommandQueue.Add(cc);
 
                 cmd = GetMakeCommandFor(mcBuild, bjt.project, bt);
-                cc = new CompilerCommand(cmd, wxEmptyString, bjt.project, bt);
+                cc = new CompilerCommand(cmd, wxEmptyString, bjt.project->GetMakefileExecutionDir(), bjt.project, bt);
                 cc->mustWait = true; // wait for clean commands to finish
                 m_CommandQueue.Add(cc);
             }
@@ -2849,7 +2851,7 @@
 
         wxString cmd = GetMakeCommandFor(mcCompileFile, m_Project, bt);
         cmd.Replace(_T("$file"), fname);
-        m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, m_Project, bt));
+        m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, m_Project->GetMakefileExecutionDir(), m_Project, bt));
     }
     else
     {
@@ -3296,7 +3298,28 @@
             }
         }
         // actually log message
-        LogWarningOrError(clt, m_pBuildingProject, compiler->GetLastErrorFilename(), compiler->GetLastErrorLine(), compiler->GetLastError());
+        wxString last_error_filename = compiler->GetLastErrorFilename();
+        if (UseMake())
+        {
+            wxFileName last_error_file(last_error_filename);
+            if (!last_error_file.IsAbsolute())
+            {
+                cbProject* project = m_Project;
+                if (m_pLastBuildingTarget)
+                {
+                    project = m_pLastBuildingTarget->GetParentProject();
+                }
+                else
+                {
+                    AskForActiveProject();
+                    project = m_Project;
+                }
+                last_error_file.PrependDir(project->GetMakefileExecutionDir());
+                last_error_file.MakeRelativeTo(project->GetBasePath());
+                last_error_filename = last_error_file.GetFullPath();
+            }
+        }
+        LogWarningOrError(clt, m_pBuildingProject, last_error_filename, compiler->GetLastErrorLine(), compiler->GetLastError());
     }
 
     // add to log
Index: src/include/cbproject.h
===================================================================
--- src/include/cbproject.h (revision 5201)
+++ src/include/cbproject.h (working copy)
@@ -165,6 +165,14 @@
         /** @return True if the project is using a custom Makefile for compilation, false if not. */
         bool IsMakefileCustom(){ return m_CustomMakefile; }
 
+        /** Allow the specification of specific execution directory if the project use a custom Makefile.
+          * @param dir The directory the custom Makefile should be executed from.
+          */
+        void SetMakefileExecutionDir(const wxString& dir);
+
+        /** @return The execution directory for the custom Makefile. */
+        const wxString& GetMakefileExecutionDir() const;
+
         /** Is there a build target (virtual or real) by @c name?
           * @param name The build target's name.
           * @param virtuals_too Include virtual build targets in query.
@@ -340,7 +348,7 @@
           * @return True if succesfull, false otherwise.
           */
         bool LoadLayout();
-       
+
         /** Notify that file(s) will be added shortly.
           * This function should be called before calling AddFile().
           * When done calling AddFile() as many times as needed, call
@@ -660,7 +668,7 @@
           * instead of re-using the existing one (if any).
           */
         virtual void AddToExtensions(const wxString& stringDesc);
-       
+
         /** Internal use only.
           * Updates the internal hashmap of project files.
           */
@@ -686,6 +694,7 @@
         wxString m_DefaultExecuteTarget;
         wxString m_Makefile;
         bool m_CustomMakefile;
+        mutable wxString m_MakefileExecutionDir;
 
         FilesList m_Files;
         wxArrayString m_ExpandedNodes;


Offline lexis

  • Multiple posting newcomer
  • *
  • Posts: 36
Hi to CB team!

As I see at BerliOS the patch is assigned to jens from January
When is the patch planned to be applied to trunk and build at nightly?

It will be very useful for custom build system where you have no control on directory tree.

Offline Jenna

  • Administrator
  • Lives here!
  • *****
  • Posts: 7256
Hi to CB team!

As I see at BerliOS the patch is assigned to jens from January
When is the patch planned to be applied to trunk and build at nightly?

It will be very useful for custom build system where you have no control on directory tree.

Thanks for remembering me.

There have been other things I had to work on, and very much work at the job at the moment, so it has not top priority.

The patch has to be fixed to work with the new makefile-handling (in fact I have done it already) and I am in testing phase.

So please be patient.

I hope I can upload a modified patch for testing purposes the next days.

Offline Jenna

  • Administrator
  • Lives here!
  • *****
  • Posts: 7256
committed in slightly modified form (svn r5497)

Offline gryphon

  • Multiple posting newcomer
  • *
  • Posts: 15
Hi Jens,

Once again I have to thank you making the effort to add one of my patches. I've actually been doing quite a lot of work on makefile support as we use this daily. Hopefully after I merge your merged patch to my current development branch I'll be in a position to get some of my other changes made into a separate patch.

Actually I wonder would it be possible at all to have access to a separate codeblocks branch in svn for makefile support? In my current organisation we all use a more heavily patched codeblocks for daily development. This is based on my current work which unfortunately is not under branched version control (I should really try svk). If there is interest in more work related to custom makefiles having a branch in the project for this might be useful as it would allow others to more easily share and try the capabilities. Don't worry - if that's not possible I'll still work on producing patches, it's just a little more complex.

Thanks again,
gryphon