2016-08-12 15:54:47 -04:00
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// author:
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------
# include "cling/Utils/Paths.h"
2016-09-10 15:04:39 -04:00
# include "cling/Utils/Output.h"
2016-09-08 17:15:38 -04:00
# include "clang/Basic/FileManager.h"
2016-08-12 22:02:50 -04:00
# include "clang/Lex/HeaderSearchOptions.h"
# include "llvm/Support/ErrorHandling.h"
2016-08-12 15:54:47 -04:00
# include "llvm/Support/FileSystem.h"
2016-09-08 17:15:38 -04:00
# include "llvm/Support/Path.h"
2016-08-12 15:54:47 -04:00
namespace cling {
namespace utils {
2016-09-19 14:23:17 -04:00
namespace platform {
# if defined(LLVM_ON_UNIX)
const char * const kEnvDelim = " : " ;
# elif defined(LLVM_ON_WIN32)
const char * const kEnvDelim = " ; " ;
# else
# error "Unknown platform (environmental delimiter)"
# endif
2016-10-03 19:54:24 -04:00
} // namespace platform
bool ExpandEnvVars ( std : : string & Str , bool Path ) {
std : : size_t DPos = Str . find ( " $ " ) ;
while ( DPos ! = std : : string : : npos ) {
std : : size_t SPos = Str . find ( " / " , DPos + 1 ) ;
std : : size_t Length = Str . length ( ) ;
if ( SPos ! = std : : string : : npos ) // if we found a "/"
Length = SPos - DPos ;
std : : string EnvVar = Str . substr ( DPos + 1 , Length - 1 ) ; //"HOME"
std : : string FullPath ;
if ( const char * Tok = : : getenv ( EnvVar . c_str ( ) ) )
FullPath = Tok ;
Str . replace ( DPos , Length , FullPath ) ;
DPos = Str . find ( " $ " , DPos + 1 ) ; //search for next env variable
}
if ( ! Path )
return true ;
return llvm : : sys : : fs : : exists ( Str . c_str ( ) ) ;
2016-09-19 14:23:17 -04:00
}
2016-08-12 22:02:50 -04:00
using namespace clang ;
// Adapted from clang/lib/Frontend/CompilerInvocation.cpp
void CopyIncludePaths ( const clang : : HeaderSearchOptions & Opts ,
llvm : : SmallVectorImpl < std : : string > & incpaths ,
bool withSystem , bool withFlags ) {
if ( withFlags & & Opts . Sysroot ! = " / " ) {
incpaths . push_back ( " -isysroot " ) ;
incpaths . push_back ( Opts . Sysroot ) ;
}
/// User specified include entries.
for ( unsigned i = 0 , e = Opts . UserEntries . size ( ) ; i ! = e ; + + i ) {
const HeaderSearchOptions : : Entry & E = Opts . UserEntries [ i ] ;
if ( E . IsFramework & & E . Group ! = frontend : : Angled )
llvm : : report_fatal_error ( " Invalid option set! " ) ;
switch ( E . Group ) {
case frontend : : After :
if ( withFlags ) incpaths . push_back ( " -idirafter " ) ;
break ;
case frontend : : Quoted :
if ( withFlags ) incpaths . push_back ( " -iquote " ) ;
break ;
case frontend : : System :
if ( ! withSystem ) continue ;
if ( withFlags ) incpaths . push_back ( " -isystem " ) ;
break ;
case frontend : : IndexHeaderMap :
if ( ! withSystem ) continue ;
if ( withFlags ) incpaths . push_back ( " -index-header-map " ) ;
if ( withFlags ) incpaths . push_back ( E . IsFramework ? " -F " : " -I " ) ;
break ;
case frontend : : CSystem :
if ( ! withSystem ) continue ;
if ( withFlags ) incpaths . push_back ( " -c-isystem " ) ;
break ;
case frontend : : ExternCSystem :
if ( ! withSystem ) continue ;
if ( withFlags ) incpaths . push_back ( " -extern-c-isystem " ) ;
break ;
case frontend : : CXXSystem :
if ( ! withSystem ) continue ;
if ( withFlags ) incpaths . push_back ( " -cxx-isystem " ) ;
break ;
case frontend : : ObjCSystem :
if ( ! withSystem ) continue ;
if ( withFlags ) incpaths . push_back ( " -objc-isystem " ) ;
break ;
case frontend : : ObjCXXSystem :
if ( ! withSystem ) continue ;
if ( withFlags ) incpaths . push_back ( " -objcxx-isystem " ) ;
break ;
case frontend : : Angled :
if ( withFlags ) incpaths . push_back ( E . IsFramework ? " -F " : " -I " ) ;
break ;
}
incpaths . push_back ( E . Path ) ;
}
if ( withSystem & & ! Opts . ResourceDir . empty ( ) ) {
if ( withFlags ) incpaths . push_back ( " -resource-dir " ) ;
incpaths . push_back ( Opts . ResourceDir ) ;
}
if ( withSystem & & withFlags & & ! Opts . ModuleCachePath . empty ( ) ) {
incpaths . push_back ( " -fmodule-cache-path " ) ;
incpaths . push_back ( Opts . ModuleCachePath ) ;
}
if ( withSystem & & withFlags & & ! Opts . UseStandardSystemIncludes )
incpaths . push_back ( " -nostdinc " ) ;
if ( withSystem & & withFlags & & ! Opts . UseStandardCXXIncludes )
incpaths . push_back ( " -nostdinc++ " ) ;
if ( withSystem & & withFlags & & Opts . UseLibcxx )
incpaths . push_back ( " -stdlib=libc++ " ) ;
if ( withSystem & & withFlags & & Opts . Verbose )
incpaths . push_back ( " -v " ) ;
}
void DumpIncludePaths ( const clang : : HeaderSearchOptions & Opts ,
llvm : : raw_ostream & Out ,
bool WithSystem , bool WithFlags ) {
llvm : : SmallVector < std : : string , 100 > IncPaths ;
CopyIncludePaths ( Opts , IncPaths , WithSystem , WithFlags ) ;
// print'em all
for ( unsigned i = 0 ; i < IncPaths . size ( ) ; + + i ) {
Out < < IncPaths [ i ] < < " \n " ;
}
}
2016-08-16 18:05:45 -04:00
void LogNonExistantDirectory ( llvm : : StringRef Path ) {
2016-09-10 15:04:39 -04:00
cling : : log ( ) < < " ignoring nonexistent directory \" " < < Path < < " \" \n " ;
2016-08-16 18:05:45 -04:00
}
2016-09-08 17:15:38 -04:00
static void LogFileStatus ( const char * Prefix , const char * FileType ,
llvm : : StringRef Path ) {
cling : : log ( ) < < Prefix < < " " < < FileType < < " ' " < < Path < < " ' \n " ;
}
bool LookForFile ( const std : : vector < const char * > & Args , std : : string & Path ,
const clang : : FileManager * FM , const char * FileType ) {
if ( llvm : : sys : : fs : : is_regular_file ( Path ) ) {
if ( FileType )
LogFileStatus ( " Using " , FileType , Path ) ;
return true ;
}
if ( FileType )
LogFileStatus ( " Ignoring " , FileType , Path ) ;
SmallString < 1024 > FilePath ;
if ( FM ) {
FilePath . assign ( Path ) ;
if ( FM - > FixupRelativePath ( FilePath ) & &
llvm : : sys : : fs : : is_regular_file ( FilePath ) ) {
if ( FileType )
LogFileStatus ( " Using " , FileType , FilePath . str ( ) ) ;
Path = FilePath . str ( ) ;
return true ;
}
// Don't write same same log entry twice when FilePath == Path
if ( FileType & & ! FilePath . str ( ) . equals ( Path ) )
LogFileStatus ( " Ignoring " , FileType , FilePath ) ;
}
else if ( llvm : : sys : : path : : is_absolute ( Path ) )
return false ;
for ( std : : vector < const char * > : : const_iterator It = Args . begin ( ) ,
End = Args . end ( ) ; It < End ; + + It ) {
const char * Arg = * It ;
// TODO: Suppport '-iquote' and MSVC equivalent
if ( ! : : strncmp ( " -I " , Arg , 2 ) | | ! : : strncmp ( " /I " , Arg , 2 ) ) {
if ( ! Arg [ 2 ] ) {
if ( + + It > = End )
break ;
FilePath . assign ( * It ) ;
}
else
FilePath . assign ( Arg + 2 ) ;
llvm : : sys : : path : : append ( FilePath , Path . c_str ( ) ) ;
if ( llvm : : sys : : fs : : is_regular_file ( FilePath ) ) {
if ( FileType )
LogFileStatus ( " Using " , FileType , FilePath . str ( ) ) ;
Path = FilePath . str ( ) ;
return true ;
}
if ( FileType )
LogFileStatus ( " Ignoring " , FileType , FilePath ) ;
}
}
return false ;
}
2016-08-12 15:54:47 -04:00
bool SplitPaths ( llvm : : StringRef PathStr ,
2016-08-16 05:46:10 -04:00
llvm : : SmallVectorImpl < llvm : : StringRef > & Paths ,
2016-08-16 18:05:45 -04:00
SplitMode Mode , llvm : : StringRef Delim , bool Verbose ) {
2016-09-13 16:24:40 -04:00
assert ( Delim . size ( ) & & " Splitting without a delimiter " ) ;
# if defined(LLVM_ON_WIN32)
// Support using a ':' delimiter on Windows.
2016-09-07 01:19:18 -04:00
const bool WindowsColon = Delim . equals ( " : " ) ;
# endif
2016-09-13 16:24:40 -04:00
bool AllExisted = true ;
2016-08-12 15:54:47 -04:00
for ( std : : pair < llvm : : StringRef , llvm : : StringRef > Split = PathStr . split ( Delim ) ;
! Split . second . empty ( ) ; Split = PathStr . split ( Delim ) ) {
2016-08-16 18:05:45 -04:00
2016-09-07 01:10:50 -04:00
if ( ! Split . first . empty ( ) ) {
2016-09-07 01:19:18 -04:00
bool Exists = llvm : : sys : : fs : : is_directory ( Split . first ) ;
2016-09-13 16:24:40 -04:00
# if defined(LLVM_ON_WIN32)
// Because drive letters will have a colon we have to make sure the split
// occurs at a colon not followed by a path separator.
2016-09-07 01:19:18 -04:00
if ( ! Exists & & WindowsColon & & Split . first . size ( ) = = 1 ) {
2016-09-13 16:24:40 -04:00
// Both clang and cl.exe support '\' and '/' path separators.
if ( Split . second . front ( ) = = ' \\ ' | | Split . second . front ( ) = = ' / ' ) {
const std : : pair < llvm : : StringRef , llvm : : StringRef > Tmp =
Split . second . split ( Delim ) ;
// Split.first = 'C', but we want 'C:', so Tmp.first.size()+2
Split . first =
llvm : : StringRef ( Split . first . data ( ) , Tmp . first . size ( ) + 2 ) ;
Split . second = Tmp . second ;
Exists = llvm : : sys : : fs : : is_directory ( Split . first ) ;
}
2016-09-07 01:19:18 -04:00
}
2016-09-13 16:24:40 -04:00
# endif
2016-09-07 01:19:18 -04:00
2016-09-07 01:10:50 -04:00
AllExisted = AllExisted & & Exists ;
if ( ! Exists ) {
if ( Mode = = kFailNonExistant ) {
if ( Verbose ) {
// Exiting early, but still log all non-existant paths that we have
LogNonExistantDirectory ( Split . first ) ;
while ( ! Split . second . empty ( ) ) {
Split = PathStr . split ( Delim ) ;
if ( llvm : : sys : : fs : : is_directory ( Split . first ) ) {
2016-09-10 15:04:39 -04:00
cling : : log ( ) < < " ignoring directory that exists \" "
2016-09-07 01:10:50 -04:00
< < Split . first < < " \" \n " ;
} else
LogNonExistantDirectory ( Split . first ) ;
Split = Split . second . split ( Delim ) ;
}
if ( ! llvm : : sys : : fs : : is_directory ( Split . first ) )
2016-08-16 18:05:45 -04:00
LogNonExistantDirectory ( Split . first ) ;
}
2016-09-07 01:10:50 -04:00
return false ;
} else if ( Mode = = kAllowNonExistant )
Paths . push_back ( Split . first ) ;
else if ( Verbose )
LogNonExistantDirectory ( Split . first ) ;
} else
Paths . push_back ( Split . first ) ;
2016-08-16 18:05:45 -04:00
}
2016-08-16 05:46:10 -04:00
2016-08-12 15:54:47 -04:00
PathStr = Split . second ;
}
2016-09-13 16:24:40 -04:00
// Trim trailing sep in case of A:B:C:D:
if ( ! PathStr . empty ( ) & & PathStr . endswith ( Delim ) )
PathStr = PathStr . substr ( 0 , PathStr . size ( ) - Delim . size ( ) ) ;
2016-09-07 01:19:18 -04:00
if ( ! PathStr . empty ( ) ) {
if ( ! llvm : : sys : : fs : : is_directory ( PathStr ) ) {
AllExisted = false ;
if ( Mode = = kAllowNonExistant )
Paths . push_back ( PathStr ) ;
else if ( Verbose )
LogNonExistantDirectory ( PathStr ) ;
} else
2016-08-16 05:46:10 -04:00
Paths . push_back ( PathStr ) ;
2016-09-07 01:19:18 -04:00
}
2016-08-12 15:54:47 -04:00
return AllExisted ;
}
2016-08-16 05:46:10 -04:00
void AddIncludePaths ( llvm : : StringRef PathStr , clang : : HeaderSearchOptions & HOpts ,
const char * Delim ) {
llvm : : SmallVector < llvm : : StringRef , 10 > Paths ;
if ( Delim & & * Delim )
2016-08-16 18:05:45 -04:00
SplitPaths ( PathStr , Paths , kAllowNonExistant , Delim , HOpts . Verbose ) ;
2016-08-16 05:46:10 -04:00
else
Paths . push_back ( PathStr ) ;
// Avoid duplicates
llvm : : SmallVector < llvm : : StringRef , 10 > PathsChecked ;
for ( llvm : : StringRef Path : Paths ) {
bool Exists = false ;
for ( const clang : : HeaderSearchOptions : : Entry & E : HOpts . UserEntries ) {
if ( ( Exists = E . Path = = Path ) )
break ;
}
if ( ! Exists )
PathsChecked . push_back ( Path ) ;
}
const bool IsFramework = false ;
const bool IsSysRootRelative = true ;
for ( llvm : : StringRef Path : PathsChecked )
HOpts . AddPath ( Path , clang : : frontend : : Angled ,
IsFramework , IsSysRootRelative ) ;
2016-08-16 18:05:45 -04:00
if ( HOpts . Verbose ) {
2016-09-10 15:04:39 -04:00
cling : : log ( ) < < " Added include paths: \n " ;
2016-08-16 18:05:45 -04:00
for ( llvm : : StringRef Path : PathsChecked )
2016-09-10 15:04:39 -04:00
cling : : log ( ) < < " " < < Path < < " \n " ;
2016-08-16 18:05:45 -04:00
}
2016-08-16 05:46:10 -04:00
}
2016-08-12 22:02:50 -04:00
2016-08-12 15:54:47 -04:00
} // namespace utils
} // namespace cling