File: CompileCommandsGenerator.cpp

package info (click to toggle)
codelite 17.0.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 136,204 kB
  • sloc: cpp: 491,547; ansic: 280,393; php: 10,259; sh: 8,930; lisp: 7,664; vhdl: 6,518; python: 6,020; lex: 4,920; yacc: 3,123; perl: 2,385; javascript: 1,715; cs: 1,193; xml: 1,110; makefile: 804; cobol: 741; sql: 709; ruby: 620; f90: 566; ada: 534; asm: 464; fortran: 350; objc: 289; tcl: 258; java: 157; erlang: 61; pascal: 51; ml: 49; awk: 44; haskell: 36
file content (175 lines) | stat: -rw-r--r-- 6,491 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#include "CompileCommandsGenerator.h"

#include "CompileCommandsJSON.h"
#include "CompileFlagsTxt.h"
#include "JSON.h"
#include "clFileSystemWorkspace.hpp"
#include "clFilesCollector.h"
#include "cl_config.h"
#include "clcommandlineparser.h"
#include "compiler_command_line_parser.h"
#include "environmentconfig.h"
#include "event_notifier.h"
#include "file_logger.h"
#include "fileutils.h"
#include "globals.h"
#include "imanager.h"
#include "processreaderthread.h"
#include "workspace.h"
#include "wxmd5.h"

#include <macros.h>
#include <thread>

wxDEFINE_EVENT(wxEVT_COMPILE_COMMANDS_JSON_GENERATED, clCommandEvent);

CompileCommandsGenerator::CompileCommandsGenerator()
{
    Bind(wxEVT_ASYNC_PROCESS_TERMINATED, &CompileCommandsGenerator::OnProcessTeraminated, this);
    Bind(wxEVT_ASYNC_PROCESS_OUTPUT, &CompileCommandsGenerator::OnProcessOutput, this);
}

CompileCommandsGenerator::~CompileCommandsGenerator()
{
    // If the child process is still running, detach from it. i.e. OnProcessTeraminated() event is not called
    Unbind(wxEVT_ASYNC_PROCESS_TERMINATED, &CompileCommandsGenerator::OnProcessTeraminated, this);
    Unbind(wxEVT_ASYNC_PROCESS_OUTPUT, &CompileCommandsGenerator::OnProcessOutput, this);
    if(m_process) {
        m_process->Detach();
    }
    wxDELETE(m_process);
}

typedef wxString CheckSum_t;
static CheckSum_t ComputeFileCheckSum(const wxFileName& fn) { return wxMD5::GetDigest(fn); }

void CompileCommandsGenerator::OnProcessOutput(clProcessEvent& event) { m_capturedOutput << event.GetOutput(); }

void CompileCommandsGenerator::OnProcessTeraminated(clProcessEvent& event)
{
    // dont call event.Skip() so we will delete the m_process ourself
    wxDELETE(m_process);
    clGetManager()->SetStatusMessage(_("Ready"));

    wxArrayString generated_paths = ::wxStringTokenize(m_capturedOutput, "\n\r", wxTOKEN_STRTOK);
    m_capturedOutput.clear();

    static std::unordered_map<wxString, CheckSum_t> m_checksumCache;

    bool generateCompileCommands = true;
    generateCompileCommands = clConfig::Get().Read(wxString("GenerateCompileCommands"), generateCompileCommands);

    // Process the compile_flags.txt files starting from the "compile_commands.json" root folder
    // Notify about completion
    std::thread thr(
        [=](const wxArrayString& paths) {
            clDEBUG() << "Checking paths for changes:" << paths << endl;
            bool event_is_needed = false;
            for(const wxString& path : paths) {
                // Calculate the new file checksum
                CheckSum_t ck = ComputeFileCheckSum(path);
                CheckSum_t oldCk;
                clDEBUG() << "New checksum is: [" << ck << "]" << endl;
                if(m_checksumCache.count(path)) {
                    oldCk = m_checksumCache.find(path)->second;
                } else {
                    clDEBUG() << "File:" << path << "is not found in the cache";
                }
                clDEBUG() << "Old checksum is: [" << oldCk << "]";
                bool file_exists = wxFileName::FileExists(path);
                clDEBUG() << "File:" << path << "exists:" << file_exists << endl;

                if(ck != oldCk || !file_exists) {
                    event_is_needed = true;
                }

                // update the checksum in the cache
                m_checksumCache.erase(path);
                m_checksumCache.insert({ path, ck });
            }

            if(!event_is_needed) {
                clDEBUG() << "No changes detected for paths:" << paths << endl;
                // We fire this event with empty content. This ensures that
                // a LSP restart will take place
                // clCommandEvent eventCompileCommandsGenerated(wxEVT_COMPILE_COMMANDS_JSON_GENERATED);
                // EventNotifier::Get()->QueueEvent(eventCompileCommandsGenerated.Clone());
                return;
            }

            // Notify about it
            clCommandEvent eventCompileCommandsGenerated(wxEVT_COMPILE_COMMANDS_JSON_GENERATED);
            eventCompileCommandsGenerated.SetStrings(paths);
            EventNotifier::Get()->QueueEvent(eventCompileCommandsGenerated.Clone());
        },
        generated_paths);
    thr.detach();
}

void CompileCommandsGenerator::GenerateCompileCommands()
{
    // Kill any previous process running
    if(m_process) {
        m_process->Detach();
    }
    wxDELETE(m_process);

    if(!clCxxWorkspaceST::Get()->IsOpen()) {
        return;
    }
    if(!clCxxWorkspaceST::Get()->GetActiveProject()) {
        return;
    }

    wxFileName codeliteMake(clStandardPaths::Get().GetBinFolder(), "codelite-make");
#ifdef __WXMSW__
    codeliteMake.SetExt("exe");
#endif

    if(!codeliteMake.FileExists()) {
        clWARNING() << "Could not find" << codeliteMake;
        return;
    }

    wxString command;
    command << codeliteMake.GetFullPath();
    ::WrapWithQuotes(command);

    wxString workspaceFile = clCxxWorkspaceST::Get()->GetFileName();
    ::WrapWithQuotes(workspaceFile);

    wxString configName =
        clCxxWorkspaceST::Get()->GetSelectedConfig() ? clCxxWorkspaceST::Get()->GetSelectedConfig()->GetName() : "";

    bool generateCompileCommands = true;
    generateCompileCommands = clConfig::Get().Read(wxString("GenerateCompileCommands"), generateCompileCommands);

    command << " --workspace=" << workspaceFile;

    // if we are required to generate compile_commands.json, pass the --json flags
    // not passing it means only compile_flags.txt files are generated
    if(generateCompileCommands) {
        command << " --json ";
    } else {
        command << " --compile-flags ";
    }
    command << " --config=" << configName;

    // since we might be activated with a different settings directory
    // pass the build_settings.xml to codelite-make

    wxFileName xmlFile(clStandardPaths::Get().GetUserDataDir(), "build_settings.xml");
    xmlFile.AppendDir("config");
    wxString xmlPath = xmlFile.GetFullPath();
    ::WrapWithQuotes(xmlPath);
    command << " --settings=" << xmlPath;

    clDEBUG() << "Executing:" << command;

    EnvSetter env(clCxxWorkspaceST::Get()->GetActiveProject());
    m_process = ::CreateAsyncProcess(this, command, IProcessCreateDefault | IProcessWrapInShell);
    m_capturedOutput.clear();

    wxString filename = generateCompileCommands ? wxString("compile_commands.json") : wxString("compile_flags.txt");
    clGetManager()->SetStatusMessage(wxString() << _("Generating ") << filename, 2);
}