1 /*
2  * SessionRenv.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 "SessionRenv.hpp"
17 
18 #include <shared_core/Error.hpp>
19 #include <core/Exec.hpp>
20 
21 #include <r/RExec.hpp>
22 #include <r/RJson.hpp>
23 
24 #include <session/SessionModuleContext.hpp>
25 #include <session/projects/SessionProjects.hpp>
26 
27 
28 using namespace rstudio::core;
29 
30 namespace rstudio {
31 namespace session {
32 namespace module_context {
33 
isRequiredRenvInstalled()34 bool isRequiredRenvInstalled()
35 {
36    return isPackageVersionInstalled("renv", "0.9.2");
37 }
38 
isRenvActive()39 bool isRenvActive()
40 {
41    return !core::system::getenv("RENV_PROJECT").empty();
42 }
43 
44 namespace {
45 
renvStateAsJson(const std::string method)46 core::json::Value renvStateAsJson(const std::string method)
47 {
48    json::Value resultJson;
49    Error error =
50          r::exec::RFunction(method)
51          .call(&resultJson);
52 
53    if (error)
54    {
55       LOG_ERROR(error);
56       return json::Object();
57    }
58 
59    if (resultJson.getType() != json::Type::OBJECT)
60    {
61       error = systemError(boost::system::errc::invalid_argument, ERROR_LOCATION);
62       LOG_ERROR(error);
63       return json::Object();
64    }
65 
66    return resultJson;
67 
68 }
69 
70 } // end anonymous namespace
71 
renvOptionsAsJson()72 core::json::Value renvOptionsAsJson()
73 {
74    return renvStateAsJson(".rs.renv.options");
75 }
76 
renvContextAsJson()77 core::json::Value renvContextAsJson()
78 {
79    return renvStateAsJson(".rs.renv.context");
80 }
81 
82 } // end namespace module_context
83 } // end namespace session
84 } // end namespace rstudio
85 
86 namespace rstudio {
87 namespace session {
88 namespace modules {
89 namespace renv {
90 
91 namespace {
92 
onConsolePrompt(const std::string &)93 void onConsolePrompt(const std::string& /* prompt */)
94 {
95    // use RENV_PROJECT environment variable to detect if renv active
96    std::string renvProject = core::system::getenv("RENV_PROJECT");
97    if (renvProject.empty())
98       return;
99 
100    // validate that it matches the project currently open in RStudio
101    // (we could consider relaxing this in the future)
102    const FilePath& projDir = projects::projectContext().directory();
103    if (!projDir.isEquivalentTo(FilePath(renvProject)))
104       return;
105 
106    Error error = r::exec::RFunction(".rs.renv.refresh") .call();
107    if (error)
108       LOG_ERROR(error);
109 }
110 
111 } // end anonymous namespace
112 
initialize()113 Error initialize()
114 {
115    using namespace module_context;
116 
117    // initialize renv after session init (need to make sure
118    // all other RStudio startup code runs first)
119    events().onConsolePrompt.connect(onConsolePrompt);
120 
121    using boost::bind;
122    ExecBlock initBlock;
123    initBlock.addFunctions()
124          (bind(sourceModuleRFile, "SessionRenv.R"));
125 
126    return initBlock.execute();
127 }
128 
129 } // end namespace renv
130 } // end namespace modules
131 } // end namespace session
132 } // end namespace rstudio
133