1 /*
2 * RSourceManager.cpp
3 *
4 * Copyright (C) 2021 by RStudio, PBC
5 *
6 * Unless you have received this program directly from RStudio pursuant
7 * to the terms of a commercial license agreement with RStudio, then
8 * this program is licensed to you under the terms of version 3 of the
9 * GNU Affero General Public License. This program is distributed WITHOUT
10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13 *
14 */
15
16 #include <r/RSourceManager.hpp>
17
18 #include <algorithm>
19
20 #include <boost/algorithm/string/replace.hpp>
21 #include <boost/bind/bind.hpp>
22
23 #include <shared_core/Error.hpp>
24 #include <shared_core/FilePath.hpp>
25 #include <core/Log.hpp>
26
27 #include <r/RExec.hpp>
28
29 using namespace rstudio::core;
30 using namespace boost::placeholders;
31
32 namespace rstudio {
33 namespace r {
34
35
sourceManager()36 SourceManager& sourceManager()
37 {
38 static SourceManager instance;
39 return instance;
40 }
41
sourceTools(const core::FilePath & filePath)42 Error SourceManager::sourceTools(const core::FilePath& filePath)
43 {
44 if (!filePath.exists())
45 return fileNotFoundError(filePath, ERROR_LOCATION);
46
47 Error error = sourceLocal(filePath);
48 if (error)
49 return error;
50
51 toolsFilePaths_.push_back(filePath);
52
53 return Success();
54 }
55
56
sourceLocal(const FilePath & filePath)57 Error SourceManager::sourceLocal(const FilePath& filePath)
58 {
59 return source(filePath, true);
60 }
61
ensureToolsLoaded()62 void SourceManager::ensureToolsLoaded()
63 {
64 // check whether tools:rstudio is still in the search patch
65 bool toolsRStudioLoaded = true;
66 Error error = r::exec::evaluateString("\"tools:rstudio\" %in% search()",
67 &toolsRStudioLoaded);
68 if (error)
69 LOG_ERROR(error);
70
71 // if not then source all of the tools files back in
72 if (!toolsRStudioLoaded)
73 {
74 std::for_each(toolsFilePaths_.begin(),
75 toolsFilePaths_.end(),
76 boost::bind(&SourceManager::reSourceTools, this, _1));
77 }
78 }
79
reloadIfNecessary()80 void SourceManager::reloadIfNecessary()
81 {
82 if (autoReload_)
83 {
84 std::for_each(sourcedFiles_.begin(),
85 sourcedFiles_.end(),
86 boost::bind(&SourceManager::reloadSourceIfNecessary,
87 this,
88 _1));
89 }
90 }
91
92
reSourceTools(const core::FilePath & filePath)93 void SourceManager::reSourceTools(const core::FilePath& filePath)
94 {
95 Error error = source(filePath, true);
96 if (error)
97 LOG_ERROR(error);
98 }
99
source(const FilePath & filePath,bool local)100 Error SourceManager::source(const FilePath& filePath, bool local)
101 {
102 std::string localPrefix = local ? "local(" : "";
103 std::string localParam = local ? "TRUE" : "FALSE";
104 std::string localSuffix = local ? ")" : "";
105
106 // do \ escaping (for windows)
107 std::string path = filePath.getAbsolutePath();
108 boost::algorithm::replace_all(path, "\\", "\\\\");
109
110 // Build the code. If this build is targeted for debugging, keep the source
111 // code around; otherwise, turn it off to conserve memory and expose fewer
112 // internals to the user.
113 std::string rCode = localPrefix + "source(\""
114 + path + "\", " +
115 "local=" + localParam + ", " +
116 "echo=FALSE, " +
117 "verbose=FALSE, " +
118 #ifdef NDEBUG
119 "keep.source=FALSE, " +
120 #else
121 "keep.source=TRUE, " +
122 #endif
123 "encoding='UTF-8')" + localSuffix;
124
125 // record that we sourced the file.
126 recordSourcedFile(filePath, local);
127
128 // source the file
129 return r::exec::executeString(rCode);
130 }
131
recordSourcedFile(const FilePath & filePath,bool local)132 void SourceManager::recordSourcedFile(const FilePath& filePath, bool local)
133 {
134 SourcedFileInfo fileInfo(filePath.getLastWriteTime(), local);
135 sourcedFiles_[filePath.getAbsolutePath()] = fileInfo;
136 }
137
reloadSourceIfNecessary(const SourcedFileMap::value_type & value)138 void SourceManager::reloadSourceIfNecessary(
139 const SourcedFileMap::value_type& value)
140 {
141 // extract values
142 FilePath sourcedFilePath(value.first);
143 SourcedFileInfo fileInfo = value.second;
144
145 // compare last write times and source again if necessary
146 double diffTime = std::difftime(sourcedFilePath.getLastWriteTime(),
147 fileInfo.lastWriteTime);
148 if (diffTime > 0)
149 {
150 Error error = source(sourcedFilePath, fileInfo.local);
151 if (error)
152 LOG_ERROR(error);
153 }
154 }
155
156 } // namespace r
157 } // namespace rstudio
158
159
160
161