Ejemplo n.º 1
0
void
CMakePlugin::OnExportMakefile(clBuildEvent& event)
{
    const wxString project = event.GetProjectName();
    const wxString config  = event.GetConfigurationName();

    // Get settings
    const CMakeProjectSettings* settings = GetSettingsManager()->GetProjectSettings(project, config);

    // Doesn't exists or not enabled
    if (!settings || !settings->enabled) {
        // Unable to export makefile
        event.Skip();
        return;
    }

    // Get project directory - this is directory where the makefile is stored
    const wxFileName projectDir = GetProjectDirectory(project);

    // Targets forward inspired by
    // https://gist.github.com/doitian/4978329

    // Content of the generated makefile
    wxString content = wxString() <<
        "# Generated by CMakePlugin\n"
        ".PHONY: all clean $(MAKECMDGOALS)\n"
        "\n"
    ;

    // Parent project is set
    if (!settings->parentProject.IsEmpty()) {
        // Configure parent project instead
        const wxString& parentProject = settings->parentProject;
        settings = GetSettingsManager()->GetProjectSettings(parentProject, config);

        // Parent project not found
        if (!settings || !settings->enabled) {
            CL_ERROR("Unable to find or not enabled parent project "
                "'" + parentProject + "' for project '" + project + "'");
            return;
        }

        // Get parent project directory
        wxFileName parentProjectDir = GetProjectDirectory(parentProject);
        parentProjectDir.MakeRelativeTo(projectDir.GetFullPath());

        // Path is relative so UNIX path system can be used
        const wxString parentProjectDirEsc = parentProjectDir.GetPath(wxPATH_NO_SEPARATOR, wxPATH_UNIX);

        // Redirect make to the parent project
        content <<
            "# Parent project\n"
            "PARENT          := " << parentProjectDirEsc << "\n"
            "PARENT_MAKEFILE := " << parentProject << ".mk\n"
            "\n"
            "all:\n"
            "\t$(MAKE) -C \"$(PARENT)\" -f \"$(PARENT_MAKEFILE)\" " << project << "\n"
            "\n"
            "clean:\n"
            "\t$(MAKE) -C \"$(PARENT)\" -f \"$(PARENT_MAKEFILE)\" " << project << " clean\n"
            "\n"
        ;

    } else {

        // Macro expander
        // FIXME use IMacroManager (unable to find it yet)
        MacroManager* macro = MacroManager::Instance();
        wxASSERT(macro);

        // Get variables
        // Expand variables for final project
        const wxString cmake = GetConfiguration()->GetProgramPath();
        wxFileName sourceDir = wxFileName::DirName(macro->Expand(settings->sourceDirectory, GetManager(), project, config));
        wxFileName buildDir = wxFileName::DirName(macro->Expand(settings->buildDirectory, GetManager(), project, config));

        // Source dir must be relative to build directory (here is cmake called)
        sourceDir.MakeRelativeTo(buildDir.GetFullPath());
        // Build dir must be relative to project directory
        buildDir.MakeRelativeTo(projectDir.GetFullPath());

        // Relative paths
        const wxString sourceDirEsc = sourceDir.GetPath(wxPATH_NO_SEPARATOR, wxPATH_UNIX);
        const wxString buildDirEsc = buildDir.GetPath(wxPATH_NO_SEPARATOR, wxPATH_UNIX);

        // Generated makefile
        content <<
            "CMAKE      := \"" << cmake << "\"\n"
            "BUILD_DIR  := " << buildDirEsc << "\n"
            "SOURCE_DIR := " << sourceDirEsc << "\n"
            "CMAKE_ARGS := " << CreateArguments(*settings, *m_configuration.get()) << "\n"
            "\n"
            "# Building project(s)\n"
            "$(or $(lastword $(MAKECMDGOALS)), all): $(BUILD_DIR)/Makefile\n"
            "\t$(MAKE) -C \"$(BUILD_DIR)\" $(MAKECMDGOALS)\n"
            "\n"
            "# Building directory\n"
            "$(BUILD_DIR):\n"
            "\t$(CMAKE) -E make_directory \"$(BUILD_DIR)\"\n"
            "\n"
            "# Rule that detects if cmake is called\n"
            "$(BUILD_DIR)/Makefile: .cmake_dirty | $(BUILD_DIR)\n"
            "\tcd \"$(BUILD_DIR)\" && $(CMAKE) $(CMAKE_ARGS) \"$(SOURCE_DIR)\"\n"
            "\n"
            "# This rule / file allows force cmake run\n"
            ".cmake_dirty:\n"
            "\t@echo '' > .cmake_dirty\n"
            "\n"
        ;

    }

    // Path to makefile - called project directory required
    wxFileName makefile = projectDir;
    makefile.SetName(project);
    makefile.SetExt("mk");

    // Read old content from disk
    wxString oldContent;
    bool ok = ReadFileWithConversion(makefile.GetFullPath(), oldContent);

    // Write only if there are some changes
    if (!ok || content != oldContent) {
        // Write make file content
        wxFile file(makefile.GetFullPath(), wxFile::write);

        if (!file.Write(content)) {
            CL_ERROR("Unable to write custom makefile (CMakePlugin): " + makefile.GetFullPath());
        }
    }
}
Ejemplo n.º 2
0
void CallGraph::OnShowCallGraph(wxCommandEvent& event)
{
    // myLog("wxThread::IsMain(%d)", (int)wxThread::IsMain());

    IConfigTool* config_tool = m_mgr->GetConfigTool();
    MacroManager* macro = MacroManager::Instance();
    wxASSERT(macro);

    config_tool->ReadObject(wxT("CallGraph"), &confData);

    if(!wxFileExists(GetGprofPath()) || !wxFileExists(GetDotPath()))
        return MessageBox(_T("Failed to locate required tools (gprof, dot). Please check the plugin settings."),
                          wxICON_ERROR);

    clCxxWorkspace* ws = m_mgr->GetWorkspace();
    if(!ws) return MessageBox(_("Unable to get opened workspace."), wxICON_ERROR);

    wxFileName ws_cfn = ws->GetWorkspaceFileName();

    wxString projectName = ws->GetActiveProjectName();
    
    wxString errMsg;
    ProjectPtr proj = ws->FindProjectByName( projectName, errMsg );
    wxString projPath = proj->GetProjectPath();

    BuildMatrixPtr mtx = ws->GetBuildMatrix();
    if(!mtx) return MessageBox(_("Unable to get current build matrix."), wxICON_ERROR);

    wxString build_config_name = mtx->GetSelectedConfigurationName();

    BuildConfigPtr bldConf = ws->GetProjBuildConf(projectName, build_config_name);
    if(!bldConf) return MessageBox(_("Unable to get opened workspace."), wxICON_ERROR);

    wxString projOutputFn = macro->Expand( bldConf->GetOutputFileName(), m_mgr, projectName, build_config_name );
    #ifdef __WXMSW__
    if( ! projOutputFn.Lower().EndsWith( wxT(".exe") ) ) projOutputFn += wxT(".exe");
    #endif //__WXMSW__

//    myLog("WorkspaceFileName = \"%s\"", ws_cfn.GetFullPath());
//    myLog("projectName \"%s\"", projectName);
//    myLog("build_config_name = \"%s\"", build_config_name);
//    myLog("projOutputFn = \"%s\"", projOutputFn);
//    myLog("projPath = \"%s\"", projPath);

    wxFileName cfn(projPath + wxFileName::GetPathSeparator() + projOutputFn);
    cfn.Normalize();

    // base path
    const wxString base_path = ws_cfn.GetPath();

    // check source binary exists
    wxString bin_fpath = cfn.GetFullPath();
    if(!cfn.Exists()) {
        bin_fpath = wxFileSelector("Please select the binary to analyze", base_path, "", "");
        if(bin_fpath.IsEmpty()) return MessageBox("selected binary was canceled", wxICON_ERROR);

        cfn.Assign(bin_fpath, wxPATH_NATIVE);
    }
    if(!cfn.IsFileExecutable()) return MessageBox("bin/exe isn't executable", wxICON_ERROR);

    // check 'gmon.out' file exists
    wxFileName gmon_cfn( cfn.GetPath() + wxFileName::GetPathSeparator() + GMON_FILENAME_OUT);
    gmon_cfn.Normalize();

    wxString gmonfn = gmon_cfn.GetFullPath();
    if(!gmon_cfn.Exists()) {
        gmonfn = wxFileSelector("Please select the gprof file", gmon_cfn.GetPath(), "gmon", "out");
        if(gmonfn.IsEmpty()) return MessageBox("selected gprof was canceled", wxICON_ERROR);

        gmon_cfn.Assign(gmonfn, wxPATH_NATIVE);
    }

    wxString bin, arg1, arg2;

    bin = GetGprofPath();
    arg1 = bin_fpath;
    arg2 = gmonfn;

    wxString cmdgprof = wxString::Format("%s %s %s", bin, arg1, arg2);

    // myLog("about to wxExecute(\"%s\")", cmdgprof);

    wxProcess* proc = new wxProcess(wxPROCESS_REDIRECT);

    // wxStopWatch	sw;

    const int err = ::wxExecute(cmdgprof, wxEXEC_SYNC, proc);
    // on sync returns 0 (success), -1 (failure / "couldn't be started")

    // myLog("wxExecute() returned err %d, had pid %d", err, (int)proc->GetPid());

    wxInputStream* process_is = proc->GetInputStream();
    if(!process_is || !process_is->CanRead())
        return MessageBox(_("wxProcess::GetInputStream() can't be opened, aborting"), wxICON_ERROR);

    // start parsing and writing to dot language file
    GprofParser pgp;

    pgp.GprofParserStream(process_is);

    // myLog("gprof done (read %d lines)", (int) pgp.lines.GetCount());

    delete proc;

    ConfCallGraph conf;

    config_tool->ReadObject(wxT("CallGraph"), &conf);

    DotWriter dotWriter;

    // DotWriter
    dotWriter.SetLineParser(&(pgp.lines));

    int suggestedThreshold = pgp.GetSuggestedNodeThreshold();

    if(suggestedThreshold <= conf.GetTresholdNode()) {
        suggestedThreshold = conf.GetTresholdNode();

        dotWriter.SetDotWriterFromDialogSettings(m_mgr);

    } else {
        dotWriter.SetDotWriterFromDetails(conf.GetColorsNode(),
                                          conf.GetColorsEdge(),
                                          suggestedThreshold,
                                          conf.GetTresholdEdge(),
                                          conf.GetHideParams(),
                                          conf.GetStripParams(),
                                          conf.GetHideNamespaces());

        wxString suggest_msg = wxString::Format(_("The CallGraph plugin has suggested node threshold %d to speed-up "
                                                  "the call graph creation. You can alter it on the call graph panel."),
                                                suggestedThreshold);

        MessageBox(suggest_msg, wxICON_INFORMATION);
    }

    dotWriter.WriteToDotLanguage();

    // build output dir
    cfn.Assign(base_path, "");
    cfn.AppendDir(CALLGRAPH_DIR);
    cfn.Normalize();

    if(!cfn.DirExists()) cfn.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL);

    cfn.SetFullName(DOT_FILENAME_TXT);
    wxString dot_fn = cfn.GetFullPath();

    dotWriter.SendToDotAppOutputDirectory(dot_fn);

    cfn.SetFullName(DOT_FILENAME_PNG);
    wxString output_png_fn = cfn.GetFullPath();

    // delete any existing PNG
    if(wxFileExists(output_png_fn)) wxRemoveFile(output_png_fn);

    wxString cmddot_ln;

    cmddot_ln << GetDotPath() << " -Tpng -o" << output_png_fn << " " << dot_fn;

    // myLog("wxExecute(\"%s\")", cmddot_ln);

    wxExecute(cmddot_ln, wxEXEC_SYNC | wxEXEC_HIDE_CONSOLE);

    // myLog("dot done");

    if(!wxFileExists(output_png_fn))
        return MessageBox(_("Failed to open file CallGraph.png. Please check the project settings, rebuild the project "
                            "and try again."),
                          wxICON_INFORMATION);

    // show image and create table in the editor tab page
    uicallgraphpanel* panel = new uicallgraphpanel(
        m_mgr->GetEditorPaneNotebook(), m_mgr, output_png_fn, base_path, suggestedThreshold, &(pgp.lines));

    wxString tstamp = wxDateTime::Now().Format(wxT(" %Y-%m-%d %H:%M:%S"));

    wxString title = wxT("Call graph for \"") + output_png_fn + wxT("\" " + tstamp);

    m_mgr->AddEditorPage(panel, title);
}