r-hub / r-minimal

Minimal Docker images for R
155 stars 32 forks source link

Fails to build stringfish #37

Closed eikeschott closed 1 year ago

eikeschott commented 2 years ago

Hi everyone,

I'm trying to install the qs package but the build process fails at a dependency of qs called stringfish. Running:

RUN installr -d \
    -t "R-dev file make automake autoconf linux-headers" \
    qs

gives me the following message:

--------------------------------------
Installing qs

✔ Updated metadata database: 2.56 MB in 6 files.

ℹ Updating metadata database
✔ Updating metadata database ... done

→ Will install 4 packages.
→ Will download 4 CRAN packages (5.09 MB).
+ RApiSerialize   0.1.0    [bld][cmp][dl] (6.93 kB)
+ RcppParallel    5.1.4    [bld][cmp][dl] (1.97 MB)
+ qs              0.25.1.1 [bld][cmp][dl] (1.96 MB)
+ stringfish      0.15.5   [bld][cmp][dl] (1.16 MB)
ℹ Getting 4 pkgs (5.09 MB)
✔ Got RApiSerialize 0.1.0 (source) (6.93 kB)
✔ Got qs 0.25.1.1 (source) (1.96 MB)
✔ Got RcppParallel 5.1.4 (source) (1.97 MB)
✔ Got stringfish 0.15.5 (source) (1.16 MB)
ℹ Building RApiSerialize 0.1.0
ℹ Building RcppParallel 5.1.4
✔ Built RApiSerialize 0.1.0 (1.2s)
✔ Installed RApiSerialize 0.1.0  (35ms)
✔ Built RcppParallel 5.1.4 (22.2s)
✔ Installed RcppParallel 5.1.4  (50ms)
ℹ Building stringfish 0.15.5
✖ Failed to build stringfish 0.15.5

Error: <callr_remote_error: Failed to build source package 'stringfish'>
 in process 46 
-->
Failed to build source package 'stringfish', stdout + stderr:

OE> * installing *source* package ‘stringfish’ ...
OE> ** package ‘stringfish’ successfully unpacked and MD5 sums checked
OE> staged installation is only possible with locking
OE> ** using non-staged installation
OE> checking for pkg-config... /usr/bin/pkg-config
OE> stringfish configure script
OE> PCRE2 10.36 dynamic library detected -- skipping PCRE2 compilation
OE> -lpcre2-8
OE> 
OE> 
OE> configure: creating ./config.status
OE> config.status: creating src/Makevars
OE> ** libs
OE> g++ -std=gnu++11 -I"/usr/local/lib/R/include" -DNDEBUG -DRCPP_USE_UNWIND_PROTECT -DPCRE2_CODE_UNIT_WIDTH=8 -DHAVE_CONFIG_H  -I.   -I'/usr/local/lib/R/library/Rcpp/include' -I'/usr/local/lib/R/library/RcppParallel/include' -I/usr/local/include   -fpic  -D__MUSL__  -Wall -pedantic -c RcppExports.cpp -o RcppExports.o
OE> g++ -std=gnu++11 -I"/usr/local/lib/R/include" -DNDEBUG -DRCPP_USE_UNWIND_PROTECT -DPCRE2_CODE_UNIT_WIDTH=8 -DHAVE_CONFIG_H  -I.   -I'/usr/local/lib/R/library/Rcpp/include' -I'/usr/local/lib/R/library/RcppParallel/include' -I/usr/local/include   -fpic  -D__MUSL__  -Wall -pedantic -c sf_functions.cpp -o sf_functions.o
OE> sf_functions.cpp:788:3: error: 'tbb' does not name a type
OE>   788 |   tbb::enumerable_thread_specific<iconv_wrapper> iw_latin1;
OE>       |   ^~~
OE> sf_functions.cpp:789:3: error: 'tbb' does not name a type
OE>   789 |   tbb::enumerable_thread_specific<iconv_wrapper> iw_native;
OE>       |   ^~~
OE> sf_functions.cpp:790:3: error: 'tbb' does not name a type
OE>   790 |   tbb::enumerable_thread_specific<sf::pcre2_match_wrapper> pm;
OE>       |   ^~~
OE> sf_functions.cpp: In constructor 'grepl_worker::grepl_worker(std::string, iconv_wrapper, iconv_wrapper, const sf::pcre2_match_wrapper&, RStringIndexer*, int*)':
OE> sf_functions.cpp:796:31: error: class 'grepl_worker' does not have any field named 'iw_latin1'
OE>   796 |     encode_mode(encode_mode), iw_latin1(iw_latin1), iw_native(iw_native), pm(pm), cr(cr), outptr(outptr) {}
OE>       |                               ^~~~~~~~~
OE> sf_functions.cpp:796:53: error: class 'grepl_worker' does not have any field named 'iw_native'
OE>   796 |     encode_mode(encode_mode), iw_latin1(iw_latin1), iw_native(iw_native), pm(pm), cr(cr), outptr(outptr) {}
OE>       |                                                     ^~~~~~~~~
OE> sf_functions.cpp:796:75: error: class 'grepl_worker' does not have any field named 'pm'
OE>   796 |     encode_mode(encode_mode), iw_latin1(iw_latin1), iw_native(iw_native), pm(pm), cr(cr), outptr(outptr) {}
OE>       |                                                                           ^~
OE> sf_functions.cpp: In member function 'virtual void grepl_worker::operator()(std::size_t, std::size_t)':
OE> sf_functions.cpp:799:5: error: 'tbb' has not been declared
OE>   799 |     tbb::enumerable_thread_specific<sf::pcre2_match_wrapper>::reference pm_local = pm.local();
OE>       |     ^~~
OE> sf_functions.cpp:799:60: error: expected primary-expression before '>' token
OE>   799 |     tbb::enumerable_thread_specific<sf::pcre2_match_wrapper>::reference pm_local = pm.local();
OE>       |                                                            ^
OE> sf_functions.cpp:799:63: error: '::reference' has not been declared
OE>   799 |     tbb::enumerable_thread_specific<sf::pcre2_match_wrapper>::reference pm_local = pm.local();
OE>       |                                                               ^~~~~~~~~
OE> sf_functions.cpp:800:5: error: 'tbb' has not been declared
OE>   800 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_latin1_local = iw_latin1.local();
OE>       |     ^~~
OE> sf_functions.cpp:800:50: error: expected primary-expression before '>' token
OE>   800 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_latin1_local = iw_latin1.local();
OE>       |                                                  ^
OE> sf_functions.cpp:800:53: error: '::reference' has not been declared
OE>   800 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_latin1_local = iw_latin1.local();
OE>       |                                                     ^~~~~~~~~
OE> sf_functions.cpp:801:5: error: 'tbb' has not been declared
OE>   801 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_native_local = iw_native.local();
OE>       |     ^~~
OE> sf_functions.cpp:801:50: error: expected primary-expression before '>' token
OE>   801 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_native_local = iw_native.local();
OE>       |                                                  ^
OE> sf_functions.cpp:801:53: error: '::reference' has not been declared
OE>   801 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_native_local = iw_native.local();
OE>       |                                                     ^~~~~~~~~
OE> sf_functions.cpp:809:21: error: 'pm_local' was not declared in this scope
OE>   809 |         outptr[i] = pm_local.match(q.ptr, q.len);
OE>       |                     ^~~~~~~~
OE> sf_functions.cpp:811:21: error: 'pm_local' was not declared in this scope
OE>   811 |         outptr[i] = pm_local.match(q.ptr, q.len);
OE>       |                     ^~~~~~~~
OE> sf_functions.cpp:815:25: error: 'iw_native_local' was not declared in this scope
OE>   815 |             auto istr = iw_native_local.convertToString(q.ptr, q.len);
OE>       |                         ^~~~~~~~~~~~~~~
OE> sf_functions.cpp:819:27: error: 'pm_local' was not declared in this scope
OE>   819 |               outptr[i] = pm_local.match(istr.second.c_str(), istr.second.size());
OE>       |                           ^~~~~~~~
OE> sf_functions.cpp:822:25: error: 'pm_local' was not declared in this scope
OE>   822 |             outptr[i] = pm_local.match(q.ptr, q.len);
OE>       |                         ^~~~~~~~
OE> sf_functions.cpp:825:23: error: 'iw_latin1_local' was not declared in this scope
OE>   825 |           auto istr = iw_latin1_local.convertToString(q.ptr, q.len);
OE>       |                       ^~~~~~~~~~~~~~~
OE> sf_functions.cpp:829:25: error: 'pm_local' was not declared in this scope
OE>   829 |             outptr[i] = pm_local.match(istr.second.c_str(), istr.second.size());
OE>       |                         ^~~~~~~~
OE> sf_functions.cpp:832:23: error: 'pm_local' was not declared in this scope
OE>   832 |           outptr[i] = pm_local.match(q.ptr, q.len);
OE>       |                       ^~~~~~~~
OE> sf_functions.cpp: At global scope:
OE> sf_functions.cpp:1104:3: error: 'tbb' does not name a type
OE>  1104 |   tbb::enumerable_thread_specific<iconv_wrapper> iw_latin1;
OE>       |   ^~~
OE> sf_functions.cpp:1105:3: error: 'tbb' does not name a type
OE>  1105 |   tbb::enumerable_thread_specific<iconv_wrapper> iw_native;
OE>       |   ^~~
OE> sf_functions.cpp:1106:3: error: 'tbb' does not name a type
OE>  1106 |   tbb::enumerable_thread_specific<sf::pcre2_sub_wrapper> ps;
OE>       |   ^~~
OE> sf_functions.cpp: In constructor 'gsub_worker::gsub_worker(std::string, iconv_wrapper, iconv_wrapper, const sf::pcre2_sub_wrapper&, cetype_t, cetype_t, RStringIndexer*, sf_vec_data&)':
OE> sf_functions.cpp:1114:31: error: class 'gsub_worker' does not have any field named 'iw_latin1'
OE>  1114 |     encode_mode(encode_mode), iw_latin1(iw_latin1), iw_native(iw_native), ps(ps), pattern_enc(pattern_enc), replacement_enc(replacement_enc),
OE>       |                               ^~~~~~~~~
OE> sf_functions.cpp:1114:53: error: class 'gsub_worker' does not have any field named 'iw_native'
OE>  1114 |     encode_mode(encode_mode), iw_latin1(iw_latin1), iw_native(iw_native), ps(ps), pattern_enc(pattern_enc), replacement_enc(replacement_enc),
OE>       |                                                     ^~~~~~~~~
OE> sf_functions.cpp:1114:75: error: class 'gsub_worker' does not have any field named 'ps'
OE>  1114 |     encode_mode(encode_mode), iw_latin1(iw_latin1), iw_native(iw_native), ps(ps), pattern_enc(pattern_enc), replacement_enc(replacement_enc),
OE>       |                                                                           ^~
OE> sf_functions.cpp: In member function 'virtual void gsub_worker::operator()(std::size_t, std::size_t)':
OE> sf_functions.cpp:1118:5: error: 'tbb' has not been declared
OE>  1118 |     tbb::enumerable_thread_specific<sf::pcre2_sub_wrapper>::reference ps_local = ps.local();
OE>       |     ^~~
OE> sf_functions.cpp:1118:58: error: expected primary-expression before '>' token
OE>  1118 |     tbb::enumerable_thread_specific<sf::pcre2_sub_wrapper>::reference ps_local = ps.local();
OE>       |                                                          ^
OE> sf_functions.cpp:1118:61: error: '::reference' has not been declared
OE>  1118 |     tbb::enumerable_thread_specific<sf::pcre2_sub_wrapper>::reference ps_local = ps.local();
OE>       |                                                             ^~~~~~~~~
OE> sf_functions.cpp:1119:5: error: 'tbb' has not been declared
OE>  1119 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_latin1_local = iw_latin1.local();
OE>       |     ^~~
OE> sf_functions.cpp:1119:50: error: expected primary-expression before '>' token
OE>  1119 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_latin1_local = iw_latin1.local();
OE>       |                                                  ^
OE> sf_functions.cpp:1119:53: error: '::reference' has not been declared
OE>  1119 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_latin1_local = iw_latin1.local();
OE>       |                                                     ^~~~~~~~~
OE> sf_functions.cpp:1120:5: error: 'tbb' has not been declared
OE>  1120 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_native_local = iw_native.local();
OE>       |     ^~~
OE> sf_functions.cpp:1120:50: error: expected primary-expression before '>' token
OE>  1120 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_native_local = iw_native.local();
OE>       |                                                  ^
OE> sf_functions.cpp:1120:53: error: '::reference' has not been declared
OE>  1120 |     tbb::enumerable_thread_specific<iconv_wrapper>::reference iw_native_local = iw_native.local();
OE>       |                                                     ^~~~~~~~~
OE> sf_functions.cpp:1129:27: error: 'ps_local' was not declared in this scope; did you mean 'uselocale'?
OE>  1129 |         ref[i] = sfstring(ps_local.gsub(q.ptr), CE_UTF8);
OE>       |                           ^~~~~~~~
OE>       |                           uselocale
OE> sf_functions.cpp:1132:27: error: 'ps_local' was not declared in this scope; did you mean 'uselocale'?
OE>  1132 |         ref[i] = sfstring(ps_local.gsub(q.ptr), output_enc);
OE>       |                           ^~~~~~~~
OE>       |                           uselocale
OE> sf_functions.cpp:1136:23: error: 'iw_native_local' was not declared in this scope
OE>  1136 |             bool iw = iw_native_local.convert(q.ptr, q.len, outstring);
OE>       |                       ^~~~~~~~~~~~~~~
OE> sf_functions.cpp:1140:33: error: 'ps_local' was not declared in this scope; did you mean 'uselocale'?
OE>  1140 |               ref[i] = sfstring(ps_local.gsub(outstring.c_str()), CE_UTF8);
OE>       |                                 ^~~~~~~~
OE>       |                                 uselocale
OE> sf_functions.cpp:1143:31: error: 'ps_local' was not declared in this scope; did you mean 'uselocale'?
OE>  1143 |             ref[i] = sfstring(ps_local.gsub(q.ptr), CE_UTF8);
OE>       |                               ^~~~~~~~
OE>       |                               uselocale
OE> sf_functions.cpp:1146:21: error: 'iw_latin1_local' was not declared in this scope
OE>  1146 |           bool iw = iw_latin1_local.convert(q.ptr, q.len, outstring);
OE>       |                     ^~~~~~~~~~~~~~~
OE> sf_functions.cpp:1150:31: error: 'ps_local' was not declared in this scope; did you mean 'uselocale'?
OE>  1150 |             ref[i] = sfstring(ps_local.gsub(outstring.c_str()), CE_UTF8);
OE>       |                               ^~~~~~~~
OE>       |                               uselocale
OE> sf_functions.cpp:1153:29: error: 'ps_local' was not declared in this scope; did you mean 'uselocale'?
OE>  1153 |           ref[i] = sfstring(ps_local.gsub(q.ptr), CE_UTF8);
OE>       |                             ^~~~~~~~
OE>       |                             uselocale
OE> sf_functions.cpp: At global scope:
OE> sf_functions.cpp:1348:25: error: 'tbb' does not name a type
OE>  1348 | using tbb_rstring_map = tbb::concurrent_unordered_map<RStringIndexer::rstring_info, tbb::atomic<int>, rstring_info_hash>;
OE>       |                         ^~~
OE> sf_functions.cpp:1352:3: error: 'tbb_rstring_map' does not name a type
OE>  1352 |   tbb_rstring_map * table_hash;
OE>       |   ^~~~~~~~~~~~~~~
OE> sf_functions.cpp:1354:35: error: expected ')' before '*' token
OE>  1354 |   hash_fill_worker(tbb_rstring_map * t, RStringIndexer * f) :
OE>       |                   ~               ^~
OE>       |                                   )
OE> sf_functions.cpp: In member function 'virtual void hash_fill_worker::operator()(std::size_t, std::size_t)':
OE> sf_functions.cpp:1360:16: error: 'table_hash' was not declared in this scope
OE>  1360 |       auto p = table_hash->insert(tbb_rstring_map::value_type(q,i));
OE>       |                ^~~~~~~~~~
OE> sf_functions.cpp:1360:35: error: 'tbb_rstring_map' has not been declared
OE>  1360 |       auto p = table_hash->insert(tbb_rstring_map::value_type(q,i));
OE>       |                                   ^~~~~~~~~~~~~~~
OE> sf_functions.cpp: At global scope:
OE> sf_functions.cpp:1373:3: error: 'tbb_rstring_map' does not name a type
OE>  1373 |   tbb_rstring_map * table_hash;
OE>       |   ^~~~~~~~~~~~~~~
OE> sf_functions.cpp:1376:37: error: expected ')' before '*' token
OE>  1376 |   hash_search_worker(tbb_rstring_map * t, RStringIndexer * s, int * o) :
OE>       |                     ~               ^~
OE>       |                                     )
OE> sf_functions.cpp: In member function 'virtual void hash_search_worker::operator()(std::size_t, std::size_t)':
OE> sf_functions.cpp:1382:17: error: 'table_hash' was not declared in this scope
OE>  1382 |       auto it = table_hash->find(q);
OE>       |                 ^~~~~~~~~~
OE> make: *** [/usr/local/lib/R/etc/Makeconf:177: sf_functions.o] Error 1
OE> ERROR: compilation failed for package ‘stringfish’
OE> * removing ‘/tmp/RtmpClEiCf/pkg-lib2e5be10640/stringfish’

 Stack trace:

 12. (function (...)  ...
 13. base:::withCallingHandlers(cli_message = function(msg) { ...
 14. get("pkg_install_do_plan", asNamespace("pak"))(...)
 15. pkgdepends::install_package_plan(plan = plan, lib = lib, num_workers = num_ ...
 16. base:::withCallingHandlers({ ...
 17. pkgdepends:::handle_events(state, events)
 18. pkgdepends:::handle_event(state, i)
 19. pkgdepends:::stop_task(state, worker)
 20. pkgdepends:::stop_task_build(state, worker)
 21. base:::throw(new_pkg_build_error("Failed to build source package {pkg}",  ...
 22. base:::signalCondition(cond)
 23. (function (e)  ...
 24. base:::stop(e)
 25. (function (e)  ...

 x Failed to build source package 'stringfish' 

Execution halted

--------------------------------------

I'm not too familiar with the messages I'm seeing but from what I understand it might have something to do with locales and time zone data? So I tried adding tzdata without success.

RUN apk add --no-cache tzdata
RUN export TZDIR=/usr/share/zoneinfo
RUN installr -d \
    -t "R-dev file make automake autoconf linux-headers" \
    qs

Any ideas or pointers? Thanks in advance.

gaborcsardi commented 2 years ago

I tried some obvious things, but got nowhere. Maybe it is worth reporting this for stringfish, because it fails the same way in a vanilla Alpine container:

docker run -ti alpine:latest sh
apk add R gcc g++ R-dev make linux-headers
R -q -e 'options(repos = "https://cloud.r-project.org"); install.packages("stringfish")'
eikeschott commented 2 years ago

Thank you for your quick response. I created an issue with your example over at stringfish.

traversc commented 2 years ago

I'm not familiar with alpine linux, but the error message (from the issue: https://github.com/traversc/stringfish/issues/11) suggests something with the tbb source files from RcppParallel:

../../src/tbbmalloc/proxy.cpp:27:47: error: missing binary operator before token "("
   27 | #if defined(__GLIBC_PREREQ) && !__GLIBC_PREREQ(2, 16) && _GLIBCXX_HAVE_ALIGNED_ALLOC
      |                                               ^
../../src/tbbmalloc/proxy.cpp:276:26: error: return type 'struct mallinfo' is incomplete
  276 | struct mallinfo mallinfo() __THROW
      |                          ^
... (etc)

So my suspicion is that there's an issue there. It might make sense to try to do a minimal package using tbb/RcppParallel to confirm if that is the case.

gaborcsardi commented 2 years ago

Maybe TBB is not great on musl? https://patchwork.openembedded.org/patch/171846/

traversc commented 2 years ago

I think it's likely, but I think a bit more work is needed to confirm.

What is musl/alpine?

Is there a macro #define that one can check?

gaborcsardi commented 2 years ago

There is no official #define AFAIK, but in this container we do set __MUSL__ in CFLAGS and CXXFLAGS.

traversc commented 2 years ago

I have identified the issue and it's indeed a RcppParallel issue, but thankfully not a serious one.

Edit: There are two issues:

Below describes the missing macro issue:

RcppParallel has this macro define:

#ifndef RCPP_PARALLEL_USE_TBB
# if defined(__APPLE__) || defined(__gnu_linux__) || (defined(__sun) && defined(__SVR4) && !defined(__sparc))
#  define RCPP_PARALLEL_USE_TBB 1
# else
#  define RCPP_PARALLEL_USE_TBB 0
# endif
#endif

On ubuntu, RCPP_PARALLEL_USE_TBB should usually be set to 1 because of __gnu_linux__, but on Alpine this macro isn't set.

The reason this isn't set on Alpine is the __gnu_linux__ macro is defined in glibc but not in musl: https://stackoverflow.com/questions/66037729/what-does-gnu-linux-stand-for

Here's a script to test these macros:

library(Rcpp)
sourceCpp(code = '
// [[Rcpp::depends(RcppParallel)]]
#include <RcppParallel.h>
#include <Rcpp.h>
// [[Rcpp::export]]
void test_macros() {
  std::cout << "RCPP_PARALLEL_USE_TBB = " << RCPP_PARALLEL_USE_TBB << std::endl;
#ifdef __APPLE__
  std::cout << "__APPLE__" << std::endl;
#endif
#ifdef __gnu_linux__
  std::cout << "__gnu_linux__" << std::endl;
#endif
#ifdef __sun
  std::cout << "__sun" << std::endl;
#endif
#ifdef __SVR4
  std::cout << "__SVR4" << std::endl;
#endif
#ifdef __sparc
  std::cout << "__sparc" << std::endl;
#endif
}
')

Here's a MWE that demonstrates the issue (compiles on Ubuntu, but not on Alpine).

library(Rcpp)
sourceCpp(code = '
// [[Rcpp::depends(RcppParallel)]]
#include <RcppParallel.h>
#include <Rcpp.h>
#include <cstdlib>
#if RCPP_PARALLEL_USE_TBB
#include <tbb/enumerable_thread_specific.h>
#endif
using namespace RcppParallel;
using namespace Rcpp;
struct TestWorker : public Worker {
   RVector<int> output;
   tbb::enumerable_thread_specific<std::vector<int>> thread_id;
   TestWorker(IntegerVector output) : output(output) {}
   void operator()(std::size_t begin, std::size_t end) {
    for(size_t i=begin; i<end; ++i) { 
      thread_id.local().push_back(i);
      output[i] = thread_id.local().size(); 
    }
   }
};
// [[Rcpp::export]]
IntegerVector test(size_t n) {
  IntegerVector output(n);
  TestWorker t(output);
  parallelFor(0, n, t, 1,2);
  return output;
}
')

Presumably there is a similar macro that could be checked on Alpine. It might be a good idea to ask K. Ushey's opinion, I'll leave that to you.

gaborcsardi commented 2 years ago

I believe __linux__ should be set on all Linux systems, if the goal is to detect Linux.

traversc commented 2 years ago

TBB seems to work on Alpine if you force it. Maybe there is a reason they chose to check __gnu_linux__ instead.

gaborcsardi commented 1 year ago

A year or so later, this does not seem to be a problem any more, and stringfish installs cleanly.