1ac537
From 9ca4d4aeccf50e6c036e5536ef070a09c1776817 Mon Sep 17 00:00:00 2001
1ac537
From: Stephen Gallagher <sgallagh@redhat.com>
1ac537
Date: Fri, 6 Dec 2019 16:40:25 -0500
1ac537
Subject: [PATCH 3/3] build: auto-load ICU data from
1ac537
 --with-icu-default-data-dir
1ac537
1ac537
When compiled with `--with-intl=small` and
1ac537
`--with-icu-default-data-dir=PATH`, Node.js will use PATH as a
1ac537
fallback location for the ICU data.
1ac537
1ac537
We will first perform an access check using fopen(PATH, 'r') to
1ac537
ensure that the file is readable. If it is, we'll set the
1ac537
icu_data_directory and proceed. There's a slight overhead for the
1ac537
fopen() check, but it should be barely measurable.
1ac537
1ac537
This will be useful for Linux distribution packagers who want to
1ac537
be able to ship a minimal node binary in a container image but
1ac537
also be able to add on the full i18n support where needed. With
1ac537
this patch, it becomes possible to ship the interpreter as
1ac537
/usr/bin/node in one package for the distribution and to ship the
1ac537
data files in another package (without a strict dependency
1ac537
between the two). This means that users of the distribution will
1ac537
not need to explicitly direct Node.js to locate the ICU data. It
1ac537
also means that in environments where full internationalization is
1ac537
not required, they do not need to carry the extra content (with
1ac537
the associated storage costs).
1ac537
1ac537
Refs: https://github.com/nodejs/node/issues/3460
1ac537
1ac537
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
1ac537
---
1ac537
 configure.py |  9 +++++++++
1ac537
 node.gypi    |  7 +++++++
1ac537
 src/node.cc  | 20 ++++++++++++++++++++
1ac537
 3 files changed, 36 insertions(+)
1ac537
1ac537
diff --git a/configure.py b/configure.py
1ac537
index 48389fbdcb57cbf8d9c11d4921c65f34a1937cc7..063e8748b954a7fed4fe084399e61371c061edab 100755
1ac537
--- a/configure.py
1ac537
+++ b/configure.py
1ac537
@@ -433,6 +433,14 @@ intl_optgroup.add_option('--with-icu-source',
1ac537
         'the icu4c source archive. '
1ac537
         'v%d.x or later recommended.' % icu_versions['minimum_icu'])
1ac537
1ac537
+intl_optgroup.add_option('--with-icu-default-data-dir',
1ac537
+    action='store',
1ac537
+    dest='with_icu_default_data_dir',
1ac537
+    help='Path to the icuXXdt{lb}.dat file. If unspecified, ICU data will '
1ac537
+         'only be read if the NODE_ICU_DATA environment variable or the '
1ac537
+         '--icu-data-dir runtime argument is used. This option has effect '
1ac537
+         'only when Node.js is built with --with-intl=small-icu.')
1ac537
+
1ac537
 parser.add_option('--with-ltcg',
1ac537
     action='store_true',
1ac537
     dest='with_ltcg',
1ac537
@@ -1360,6 +1368,7 @@ def configure_intl(o):
1ac537
     locs.add('root')  # must have root
1ac537
     o['variables']['icu_locales'] = string.join(locs,',')
1ac537
     # We will check a bit later if we can use the canned deps/icu-small
1ac537
+    o['variables']['icu_default_data'] = options.with_icu_default_data_dir or ''
1ac537
   elif with_intl == 'full-icu':
1ac537
     # full ICU
1ac537
     o['variables']['v8_enable_i18n_support'] = 1
1ac537
diff --git a/node.gypi b/node.gypi
1ac537
index 466a1746811cfac1a8ce4ef604ef1152c6229ff1..65b97d6466a14f4343a948a5fc36f8a2580badfb 100644
1ac537
--- a/node.gypi
1ac537
+++ b/node.gypi
1ac537
@@ -113,6 +113,13 @@
1ac537
       'conditions': [
1ac537
         [ 'icu_small=="true"', {
1ac537
           'defines': [ 'NODE_HAVE_SMALL_ICU=1' ],
1ac537
+          'conditions': [
1ac537
+            [ 'icu_default_data!=""', {
1ac537
+              'defines': [
1ac537
+                'NODE_ICU_DEFAULT_DATA_DIR="<(icu_default_data)"',
1ac537
+              ],
1ac537
+            }],
1ac537
+          ],
1ac537
       }]],
1ac537
     }],
1ac537
     [ 'node_use_bundled_v8=="true" and \
1ac537
diff --git a/src/node.cc b/src/node.cc
1ac537
index 7c0118758dfd9449283b900209b2ba8df7ddd129..c9840e3e367ca47176a17a7940a1e08eb1f56f78 100644
1ac537
--- a/src/node.cc
1ac537
+++ b/src/node.cc
1ac537
@@ -92,6 +92,7 @@
1ac537
1ac537
 #if defined(NODE_HAVE_I18N_SUPPORT)
1ac537
 #include <unicode/uvernum.h>
1ac537
+#include <unicode/utypes.h>
1ac537
 #endif
1ac537
1ac537
 #if defined(LEAK_SANITIZER)
1ac537
@@ -2643,6 +2644,25 @@ void Init(std::vector<std::string>* argv,
1ac537
   // If the parameter isn't given, use the env variable.
1ac537
   if (per_process_opts->icu_data_dir.empty())
1ac537
     SafeGetenv("NODE_ICU_DATA", &per_process_opts->icu_data_dir);
1ac537
+
1ac537
+#ifdef NODE_ICU_DEFAULT_DATA_DIR
1ac537
+  // If neither the CLI option nor the environment variable was specified,
1ac537
+  // fall back to the configured default
1ac537
+  if (per_process_opts->icu_data_dir.empty()) {
1ac537
+    // Check whether the NODE_ICU_DEFAULT_DATA_DIR contains the right data
1ac537
+    // file and can be read.
1ac537
+    static const char full_path[] =
1ac537
+        NODE_ICU_DEFAULT_DATA_DIR "/" U_ICUDATA_NAME ".dat";
1ac537
+
1ac537
+    FILE* f = fopen(full_path, "rb");
1ac537
+
1ac537
+    if (f != nullptr) {
1ac537
+      fclose(f);
1ac537
+      per_process_opts->icu_data_dir = NODE_ICU_DEFAULT_DATA_DIR;
1ac537
+    }
1ac537
+  }
1ac537
+#endif  // NODE_ICU_DEFAULT_DATA_DIR
1ac537
+
1ac537
   // Initialize ICU.
1ac537
   // If icu_data_dir is empty here, it will load the 'minimal' data.
1ac537
   if (!i18n::InitializeICUDirectory(per_process_opts->icu_data_dir)) {
1ac537
--
1ac537
2.24.1
1ac537