3

I am trying to create a tool with clang and was wondering if it is possible to inject a include file from memory to the CompilerInstance preprocessor.
My goal is to add a #include <my_globals.hpp> to my files and dynamically include this file with the appropriate content. So I have a ASTFrontendAction like this:

class MyFrontendAction : public ASTFrontendAction
{
    virtual bool BeginInvocation(CompilerInstance &ci) override{
        auto buffer = llvm::MemoryBuffer::getMemBufferCopy(...);
        ci.createFileManager();
        ci.createSourceManager(ci.getFileManager());
        ci.createPreprocessor(clang::TU_Complete);
        auto& pp = ci.getPreprocessor();
        auto& preprocessorOpts = pp.getPreprocessorOpts();
        preprocessorOpts.clearRemappedFiles();
        preprocessorOpts.addRemappedFile("my_globals.hpp", buffer.release());
        // this seams not to work
        auto& hsi = pp.getHeaderSearchInfo();
        auto& headerSearchOptions = hsi.getHeaderSearchOpts();
        headerSearchOptions.Verbose = true; // this option is used during job

    }
    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance& ci, StringRef file) override{/* do my parsing */}

};

The parsing works as long as I do not include my header file. If i do I will get a my_globals.hpp file not found.
So the addRemappedFile does not make this file visible to the preprocessor. I could add some search path but how can I indicate that this file has no path?
Can anyone give me hint how I can solve this.

mkaes
  • 13,781
  • 10
  • 52
  • 72
  • Maybe this can give you some ideas? https://github.com/burnflare/libclang-experiments There seems to be quite a bit of description of how to do something similar on the description on that page. – xaxxon Jul 07 '17 at 05:35
  • Does it make any difference if you call ci.getPreprocessorOpts() instead of pp.getPreprocessorOpts()? – jvstech Jul 08 '17 at 00:51
  • @jvstech: It seems to make no difference. If I create the PP afterwards I get some assertions, but in the end the outcome is the same. – mkaes Jul 10 '17 at 09:44
  • 1
    @mkaes I'm actively working on this same problem right now. It looks like the answer *likely* lies in `ci.getHeaderSearchOpts().AddVFSOverlayFile()`. You'll have to create a `clang::vfs::OverlayFileSystem` and a `clang::vfs::InMemoryFileSystem`, call `overlayFs->pushOverlay(memoryFS)`, then call `memoryFs->addFile("my_globals.hpp", llvm::MemoryBuffer::getMemBufferCopy(myGlobalsCode.c_str())`. I'll test this out before I actually submit it as an answer, though. – jvstech Jul 10 '17 at 10:10
  • @mkaes Looks like I was wrong... `VFSOverlayFiles` are loaded through `llvm::MemoryBuffer::getFile()` which expects a real file. :( – jvstech Jul 10 '17 at 10:17
  • @mkaes For now, I'm going to suggest using Clang's Rewriter libs along with LibTooling to actually *replace* that include line with the needed code. See: https://stackoverflow.com/questions/27029313/whats-the-right-way-to-match-includes-or-defines-using-clangs-libtooling – jvstech Jul 10 '17 at 10:34
  • 1
    @jvstech: I solved the problem by using my own `FileSystem` implementation. Since this is too long for a comment I answered my own question. Maybe it is helpful for your problem too. – mkaes Jul 11 '17 at 13:41

1 Answers1

5

I will answer my own question maybe it will be helpful. While the PPCallbacks are called whenever the preprocessor touches a file I found no way to change the content. So my solution is to provide my own FileSystem

class MyFs : public FileSystem{
    ... openFileForRead(const Twine& Path) overwrite
    {
    // in here I create my own File that will contain the data
    // all other functions and when the path is not what I expect I forward it to a FileSystrem
    }
};
class MyFrontendAction : public ASTFrontendAction
{
    virtual bool BeginInvocation(CompilerInstance &ci) override{
        ci.createFileManager(); // now I have a default FS
        llvm::IntrusiveRefCntPtr<vfs::FileSystem> fs(new MyFs(ci.getFileManager().getVirtualFileSystem()));
        ci.setVirtualFileSystem(fs);
        ci.setFileManager(new clang::FileManager(clang::FileSystemOptions{}, fs));
        auto& fm = ci.getFileManager();
        ci.createSourceManager(fm);
        return true;
    }
    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance& ci, StringRef file) override{/* do my parsing */}

};
mkaes
  • 13,781
  • 10
  • 52
  • 72