# file      : libicu/buildfile
# license   : Unicode License; see accompanying LICENSE file

import! [metadata] genccode = icu-tools%exe{genccode}

# Detect target endianness.
#
using autoconf

h{endian}: in{endian}

[rule_hint=c.predefs] buildfile{endian}: h{endian}
{
  c.predefs.poptions = false
  c.predefs.macros = ICU_DATA_CHAR@icu_data_char
}
% update
if ($c.id == 'msvc'       ? ($c.version.major < 19 ||      \
                             ($c.version.major == 19 &&    \
                              $c.version.minor < 20))    : \
    $c.id.type == 'clang' ? $c.version.major < 12        : \
    false)
{{
  diag c-predefs $< -> $>

  cat <<EOF >$path($>)
    icu_data_char = 'l'
    EOF
}}

./: buildfile{endian} # Make sure it gets cleaned.

if ($build.meta_operation == 'perform')
{
  update buildfile{endian}
  source $path(buildfile{endian})
}
else
  icu_data_char = 'l' # l for little-endian, b for big-endian.

assert ($icu_data_char == 'l' || $icu_data_char == 'b')

# The path to the ICU data archive.
#
datfile_path = $($genccode: genccode.datfile_$(icu_data_char)_path)

# If genccode defines a path for the ICU data archive then the icu-tools
# package is installed, in which case create a local file{} target from the
# exported path; otherwise import the exported data archive file{} target.
#
# Note that although we import `icudt<icu-data-char>.dat` the target returned
# by the icu-tools export stub will have a slightly different name (currently
# with the ICU major version number added).
#
if $null($datfile_path)
  import! datfile = icu-tools%file{icudt$(icu_data_char).dat}
else
  datfile = file{$datfile_path}

# The filename of the ICU data archive. (Note that it should have the same
# value as the data_name variable in icu-tools/build/root.build.)
#
data_name = $name($datfile)

# Note that some of the uc/unicode/*.h files are C++ headers and some are C
# headers. We will assume all of them as C headers, which is a bit of a hack
# but doing it properly is too painful.
#
./: lib{icuuc}: uc/hxx{** -unicode/*} uc/unicode/h{*} \
                uc/cxx{*}                             \
                lib{icudata}

# The ICU data library.
#
# Use the genccode tool to transform the ICU data archive (.dat) produced by
# the icu-tools package into a library-embeddable form.
#
# On non-Windows platforms (including MinGW), use genccode to produce assembly
# code containing the data archive contents that we then compile into an
# object file. On Windows, use genccode to produce the object file
# directly. Then link the object file into the data library. The upstream
# build, on the other hand, uses the pkgdata tool to do everything: produce
# the data archive, generate the assembly, compile or generate the object
# file, and link the data library. See the root README-DEV for the reason we
# do it differently.
#

# If true, generate assembly from the data archive; otherwise generate the
# object file directly.
#
asm = (!$windows || $mingw)

[rule_hint=c] lib{icudata}:

# Dependencies for the assembly case.
#
lib{icudata}: data/S{$(data_name)_dat}: include = $asm

data/S{$(data_name)_dat}: $datfile $genccode

# Dependencies for the object file case (note: Windows only).
#
libs{icudata}: data/objs{$(data_name)_dat.dll.obj}: include = (!$asm)
liba{icudata}: data/obja{$(data_name)_dat.lib.obj}:  include = (!$asm)

data/objs{$(data_name)_dat.dll.obj}: $datfile data/objs{oma} $genccode
data/obja{$(data_name)_dat.lib.obj}: $datfile data/obja{oma} $genccode

# Object file used by genccode to determine the CPU architecture. Name taken
# from upstream and stands for "OptMatchArch".
#
data/obj{oma}: data/c{oma}

# Generate the assembly source code.
#
switch $tclass, $tsys
{
  case 'linux' | 'bsd'
    asm_type = 'gcc'
  case 'macos'
    asm_type = 'gcc-darwin'
  case 'windows', 'mingw32'
    asm_type = 'gcc-mingw64'
}

# Name of the ICU data library's entrypoint (the data archive filename with
# the last character, which indicates endianess, removed).
#
data_entrypoint = $regex.replace($data_name, '(.+).$', '\1')

data/S{$(data_name)_dat}: $datfile $genccode
{{
  # --entrypoint: Library entry point name; will be suffixed with _dat.
  # --assembly:   Output assembly code.
  #
  $genccode --quiet                             \
            --destdir $directory($path($>[0]))  \
            --entrypoint $data_entrypoint       \
            --assembly $asm_type                \
            $path($<[0])
}}

# Generate the object files.
#
data/objs{$(data_name)_dat.dll.obj}: $datfile data/objs{oma} $genccode
{{
  # --object:     Output an object file (instead of assembly code).
  # --match-arch: Match the architecture (CPU, 32/64 bits) of this object
  #               file.
  # --filename:   Output file basename (.obj extension will be appended).
  #
  $genccode --quiet                             \
            --destdir $directory($path($>[0]))  \
            --entrypoint $data_entrypoint       \
            --object                            \
            --match-arch $path($<[1])           \
            --filename $(data_name)_dat.dll     \
            $path($<[0])
}}

data/obja{$(data_name)_dat.lib.obj}: $datfile data/obja{oma} $genccode
{{
  # --skip-dll-export: Don't export the ICU data entry point symbol.
  #
  $genccode --quiet                             \
            --destdir $directory($path($>[0]))  \
            --entrypoint $data_entrypoint       \
            --object                            \
            --match-arch $path($<[1])           \
            --filename $(data_name)_dat.lib     \
            --skip-dll-export                   \
            $path($<[0])
}}

# Build options.
#
# Note that the icudata library and its prerequisites (the data assembly
# source file or c{oma}) are the only C targets.
#
cc.poptions =+ "-I$src_base/uc"
cc.poptions += -DU_COMMON_IMPLEMENTATION

if $windows
  cc.poptions += -DU_PLATFORM_USES_ONLY_WIN32_API=1

# Note that upstream requests the POSIX 2004 compliance (see uc/uposixdefs.h
# for details). This results with compilation errors for some source files
# when build on MacOS with gcc (as of 9.2; GCC issues 93151 and 93469 are
# reported). We workaround the issue disabling this POSIX compliance request
# for the faulty source files (luckily the request is redundant for them).
#
if ($macos && $cxx.id == 'gcc')
  uc/obj{putil umapfile}: cxx.poptions += -D_XOPEN_SOURCE=400

obja{*}: cc.poptions += -DU_STATIC_IMPLEMENTATION

c.coptions   += $common_cc_coptions                      $lib_cc_coptions
cxx.coptions += $common_cc_coptions $common_cxx_coptions $lib_cc_coptions

switch $c.class
{
  case 'gcc'
  {
    # Disable the Clang targeting MSVC warnings.
    #
    if ($c.id == 'clang' && $tsys == 'win32-msvc')
      cc.coptions += -Wno-ignored-pragma-optimize -Wno-ignored-attributes
  }
  case 'msvc'
  {
    # Disable warnings that pop up with /W3.
    #
    c.coptions   += /wd4229
    cxx.coptions += /wd4244 /wd4996
  }
}

# Link options and libraries.
#
lib{icuuc}:
{
  cc.loptions += $lib_cc_loptions
  cxx.libs += $common_cxx_libs
}

# Link options for libicudata.
#
switch $c.class, $tclass
{
  case 'gcc', 'linux'
    libs{icudata}: c.loptions += -nodefaultlibs -nostdlib
  case 'msvc'
  {
    # Link options taken from tools/pkgdata/pkgdata.cpp.
    #
    # /NOENTRY: Required for creating a resource-only DLL that contains no
    #           executable code. Use this option to prevent LINK from linking
    #           a reference to _main into the DLL.
    #
    # /MANIFEST:NO: Do not create a side-by-side manifest file.
    #
    libs{icudata}:  c.loptions += /NOENTRY /MANIFEST:NO
  }
}

# Export options.
#
# We don't export libicudata since it is not of much use to anyone except
# libicuuc.
#
lib{icuuc}:  cc.export.poptions  = "-I$src_base/uc"
liba{icuuc}: cc.export.poptions += -DU_STATIC_IMPLEMENTATION

# See bootstrap.build for details.
#
# Note that on Windows and POSIX the data library is named quite differently
# as icudt65.dll and libicudata.so.65.1, respectivelly. We name it
# consistently as icudata-65.dll and libicudata-65.so.
#
if $version.pre_release
  lib{icudata icuuc}: bin.lib.version = @"-$version.project_id"
else
  lib{icudata icuuc}: bin.lib.version = @"-$abi_version_major" \
                                        linux@"$abi_version"

# Install headers from the uc/unicode/ subdirectory only.
#
h{*}:            install = false
hxx{*}:          install = false
uc/unicode/h{*}: install = include/unicode/
