了解R中Makevars文件的内容(宏,变量,~/.R/Makevars和pkg/src/Makevars)

85
我正在尝试理解在安装/构建自己的R软件包时,~/.R/Makevarspackage_directory/src/Makevars中设置的宏/变量的作用和关系。假设这些文件看起来像:

~/.R/Makevars

CXX = g++
CXXSTD = -std=c++11
CXXFLAGS = -fsanitize=undefined,address -fno-omit-frame-pointer

CXX98 = g++
CXX98STD = -std=c++98

CXX11 = g++
CXX11STD = -std=c++11

CXX14 = g++
CXX14STD = -std=c++14

包目录/src/Makevars

PKG_CPPFLAGS = -I../inst/include
CXX_STD = CXX11
据我所知,使用CXX可以在构建R软件包时选择C++编译器,使用CXXSTD选择标准,使用CXXFLAGS添加编译器标志。使用PKG_CPPFLAGS添加C ++预处理器的标志,并使用CXX_STD说明我们的软件包使用C++11。
我的问题如下:
  • CXXCXX98CXX11CXX14之间有什么关系?
  • 例如,如果已经暗示了C++11,则例如CXX11STD = -std=c++11的含义是什么?这是在选择-std=c++11-std=gnu++11之间吗?为了可移植性,应该避免通常使用-std=gnu++11吗?
  • CXXSTDCXXFLAGS的标志不能直接添加到CXX中,使得前三行缩减为CXX = g++ -std=c++11 -fsanitize=undefined,address -fno-omit-frame-pointer。明确指定CXXSTDCXXFLAGS有什么优点?
  • CXX_STD = CXX11是如何工作的?这里的CXX11~/.R/Makevars中的CXX11有什么关系?
  • CXXFLAGSPKG_CXXFLAGS之间有什么关系(我的示例中未包括)?
我了解编写R扩展R安装和管理中所包含的信息,但是我无法从当前的理解水平中提取更多信息来回答上述问题。
我添加了一个Rcpp标记,因为我认为这些问题的答案对于Rcpp的用户最相关,但我知道这可能与Rcpp没有直接关系,因此如果被视为适当,则可以删除该标记。
1个回答

126
Makevars文件是编写R扩展:1.2.1使用Makevars中指定的一种Make变体,它是R所特有的。你列出的许多变量被称为隐式变量。意义如下:

隐式规则告诉make如何使用惯用技术,以便在您想要使用它们时不必详细说明。

这些隐式变量决定了应该使用哪个编译器和哪些选项可用。

R中,我们关心以下默认编译器选项:

CC:用于编译C程序的程序;默认值为“cc”。

CXX:用于编译C++程序的程序;默认值为“g++”。

CPP:运行C预处理器的程序,并将结果输出到标准输出;默认值为“$(CC) -E”。

FC:用于编译或预处理Fortran和Ratfor程序的程序;默认值为“f77”。

下一组值详细说明了编译器应该使用哪些选项。通常,所有这些选项的默认值都是空字符串。

CFLAGS:给C编译器的额外标志。 CXXFLAGS:给C++编译器的额外标志。 CPPFLAGS:给C预处理器和使用它的程序(C和Fortran编译器)的额外标志。 FFLAGS:给Fortran编译器的额外标志。 LDFLAGS:当编译器被要求调用链接器'ld'时,给编译器的额外标志,例如-L。 库(-lfoo)应添加到LDLIBS变量中。 LDLIBS:给编译器在被要求调用链接器'ld'时提供的库标志或名称。LOADLIBES是一个已弃用但仍受支持的LDLIBS替代品。非库链接器标志,如-L,应放在LDFLAGS变量中。

现在,R根据不同的C++ ISO标准定义了“extra”变量。这些变量列在R Administration: Section 2.7.2 C++ SupportR Administration: Section B.7 Compile and load flags中。

CXX98 CXX98STD CXX98FLAGS CXX98PICFLAGS

CXX11 CXX11STD CXX11FLAGS CXX11PICFLAGS

CXX14 CXX14STD CXX14FLAGS CXX14PICFLAGS

CXX17 CXX17STD CXX17FLAGS CXX17PICFLAGS


说到这里,我们来解决第一个问题:

CXXCXX98CXX11 以及 CXX14 之间的关系是什么?

CXX 是使用的一般编译器选项。与此同时,R 根据检测到的编译标准定义了额外的 CXX 选项。也就是说,如果由 CXX_STD 设置为 -std=c++98CXX98 语言规范),则使用与 CXX98 相关联的编译器。同样地,对于 CXX11CXX14,也是按照同样的逻辑进行处理。有关更多详细信息,请参见 Rcpp Gallery: Using Rcpp with C++11, C++14 and C++17


例如,CXX11STD = -std=c++11的意义是确定C++11编译的适当语言标准。存在这个选项仅仅是因为如果R选择适当的C++11编译选项的版本不正确,则可以更改它。之所以存在这个选项是因为每个编译器对C++11的支持可能略有不同,如R Installation and Administration: 2.7.2 C++ Support所示:

可能没有适合C++11支持的标志,此时可以为CXX11选择不同的编译器及其相应的标志。

脚注13:

这适用于早期版本的g++(如4.2.1),以及常用的Solaris编译器CC版本。有关gcc批准的语言标准的详细信息,请参见GCC手册:3.4控制C方言的选项。此外,有关在软件包中使用C ++ 11与R的详细信息,请参见编写R扩展:第1.2.4节使用C ++ 11代码。通常,我会避免显式设置此变量。如果必须显式设置此变量,我建议选择-std=c++11,因为大多数编译器都支持此声明。

是否可以将 CXXSTDCXXFLAGS 的标志添加到 CXX 中,使前三行缩减为 CXX = g++ -std=c++11 -fsanitize=undefined,address -fno-omit-frame-pointer。显式指定 CXXSTDCXXFLAGS 有什么好处?

可行吗?是的。正确吗?不是。

为什么要有 三个 变量,每个变量都有自己的目标,当我们可以只有一个变量呢?

三个 变量工作流的优点在于每行都具有不同的作用。这样可以快速理解编译选项。因此,与将其压缩到一行上的一个变量相比(终端宽度为80),更加直观易懂。

例如:

CXX = g++ -std=c++11 -fsanitize=undefined,address -fno-omit-frame-pointer

对比

CXX = g++ 
CXX11STD = -std=c++11
CXXFLAGS = -fsanitize=undefined,address -fno-omit-frame-pointer

此外,在打包时应选择CXX_STD而非CXXSTD,如编写R扩展:1.2.4节 使用C++11代码所示。这是为了确保R将包注册为需要C++xy。另一种选择是在DESCRIPTION文件中编写属性SystemRequirements: C++xy,其中xy表示年份。请注意,需保留HTML标签。
这里的CXX_STD = CXX11是如何工作的?这里的CXX11~/.R/Makevars中的CXX11有什么关系?
这将设置使用由CXX11指定的C++11编译器进行语言的编译和链接。通过指定CXX11,您正在指定一个Make中的变量,该变量将用于根据配方编译文件。
$(OBJCXX) $(ALL_CPPFLAGS) $(ALL_OBJCXXFLAGS) -c $< -o $@

其中$(OBJCXX)代表的是CXX$(ALL_CPPFLAGS)$(R_XTRA_CPPFLAGS) $(PKG_CPPFLAGS) $(CLINK_CPPFLAGS) $(CPPFLAGS)给出,而$(ALL_OBJCXXFLAGS)包含$(PKG_OBJCXXFLAGS) $(CXXPICFLAGS) $(SHLIB_CXXFLAGS) $(OBJCXXFLAGS)

上述内容参考了/R/Makeconf.in。然而,这个程序可能在/m4/R中。


什么是 CXXFLAGSPKG_CXXFLAGS(不包括在我的例子中)之间的关系?

这两者都指定了编译器的编译选项。它们在 Makevars 中书写的顺序不同。特别地,我们有PKG_CXXFLAGS 后面放置了 CXXFLAGS总是使用最右边的选项。 因此,CXXFLAGS 优先于 PKG_CXXFLAGS

PKG_* 选项有一个简短的注释,在编写 R 扩展:第 5.5 节创建共享对象中有介绍。

补充说明

以下是 @Dominik 在此回答的评论部分提出的问题。


是这样的。在~/.R/Makevars中定义的变量会全局应用于所有包,而在/src/Makevars中定义的变量仅适用于当前包。在/src/Makevars中的值将优先于~/.R/Makevars。有些包可能会附带/src/Makevars.win,该文件专门针对Windows环境提供Makevars文件。
现在用于软件包编译标准的设置只能通过进行,并不再使用像gallery.rcpp.org/articles/simple-lambda-func-c++11中展示的。这两个标志的使用时有细微差别,特别是仅在软件包环境中操作。与其名称相反,会影响所有编译选项。因此,当引用上述Rcpp画廊文章时,您正在观察一个独立的脚本运行。为了快速进入正确模式,需要设置而不是定义。

现在,请原谅我稍微离题,讲一下关于独立编译选项的历史... 使用PKG_CXXFLAGS有点老派。事实上,在R 3.4中,首选方法是设置环境变量USE_CXX11 = "yes"。在R 3.1和R 3.3之间,标准是设置环境变量USE_CXX1X = "yes"。在这些情况之前,使用PKG_CXXFLAGS ="-std=c++11"是首选(除了Windows需要PKG_CXXFLAGS ="-std=c++0x")。


使用是否意味着使用、、和中给出的所有设置?
不是。这意味着使用以下选项设置:

太好了,感谢您提供如此出色的答案! 如果可以,我想就以下问题进行一些跟进:i)在~/.R/Makevars中定义的变量是否适用于所有软件包安装,而/src/Makevars中的变量仅适用于当前软件包? ii)现在,用于软件包的编译标准只通过CXX_STD设置,不再使用PKG_CXXFALGS,如http://gallery.rcpp.org/articles/simple-lambda-func-c++11/所示吗? iii)使用CXX_STD=CXX11是否意味着使用CXXCXXSTDCXXFALGSCXX11PICFLAGS给出的所有设置? - NoBackingDown
再次感谢您回答我的额外问题。请注意,第(iii)部分有点愚蠢,因为我在前3个变量中意外地漏掉了“11”。 - NoBackingDown
~/.R/Makevars是什么?我在Windows上。我正在使用sourceCpp()编译一个简单的模型。我想抑制-Wreorder警告。谢谢。 - Simon Woodward
1
在Windows 10中,您可以使用tools::makevars_user()找到Makevars文件。我成功地通过添加PKG_CXXFLAGS = -Wno-reorder来更改了sourceCpp()的编译器选项。 - Simon Woodward
我想知道是否可能提供一些特定系统的具体示例。我对c和c++一无所知,只想获取需要编译才能编译的软件包;在我的情况下是在centos系统上。我目前有Makevars文件,如下:CC=gcc-7.3.1 CXX=g++7.3.1 CXX_STD=CXX CXX1X=g++-7.3.1 SHLIB_CXXLD=g++-7.3.1,但当我尝试编译Rcpp时会出现错误。 - JerryN
它应该可以正常工作。最好将其作为一个新问题来解决,或者可能已经在其他地方得到了解决。 - coatless

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接