aboutsummaryrefslogtreecommitdiff
path: root/libbutl/target-triplet.mxx
blob: 23b32557f595107c2a966ab1c69ffb9f06c3c9b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// file      : libbutl/target-triplet.mxx -*- C++ -*-
// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef __cpp_modules_ts
#pragma once
#endif

// C includes.

#ifndef __cpp_lib_modules_ts
#include <string>
#include <ostream>
#endif

// Other includes.

#ifdef __cpp_modules_ts
export module butl.target_triplet;
#ifdef __cpp_lib_modules_ts
import std.core;
import std.io;
#endif
#endif

#include <libbutl/export.hxx>

LIBBUTL_MODEXPORT namespace butl
{
  // This is the ubiquitous 'target triplet' that loosely has the CPU-VENDOR-OS
  // form which, these days, quite often takes the CPU-VENDOR-OS-ABI form. Plus
  // some fields can sometimes be omitted. This looseness makes it hard to base
  // any kind of decisions on the triplet without canonicalizing it and then
  // splitting it into components. The way we are going to split it is like
  // this:
  //
  // CPU
  //
  // This one is reasonably straightforward. Note that we always expect at
  // least two components with the first being the CPU. In other words, we
  // don't try to guess what just 'mingw32' might mean like config.sub does.
  //
  // VENDOR
  //
  // This can be a machine vendor as in i686-apple-darwin8, a toolchain vendor
  // as in i686-lfs-linux-gnu, or something else as in arm-softfloat-linux-gnu.
  // Just as we think vendor is pretty irrelevant and can be ignored, comes
  // MinGW-W64 and calls itself *-w64-mingw32. While it is tempting to
  // attribute w64 to OS-ABI, the MinGW-W64 folks insist it is a (presumably
  // toolchain) vendor.
  //
  // Another example where the vendor seems to be reused for something else
  // entirely is the Intel's MIC architecture: x86_64-k1om-linux.
  //
  // To make things more regular we also convert the information-free vendor
  // names 'pc', 'unknown' and 'none' to the empty name.
  //
  // OS/KERNEL-OS/OS-ABI
  //
  // This is where things get really messy and instead of trying to guess, we
  // call the entire thing SYSTEM. Except, in certain cases, we factor out the
  // trailing version, again, to make SYSTEM easier to compare to. For example,
  // *-darwin14.5.0 becomes 'darwin' and '14.5.0'.
  //
  // Again, to make things more regular, if the first component in SYSTEM is
  // none, then it is removed (so *-none-eabi becomes just 'eabi').
  //
  // Values for two-component systems (e.g., linux-gnu) that don't specify
  // VENDOR explicitly are inherently ambiguous: is 'linux' VENDOR or part of
  // SYSTEM? The only way to handle this is to recognize their specific names
  // as special cases and this is what we do for some of the more common
  // ones. The alternative would be to first run such names through config.sub
  // which adds explicit VENDOR and this could be a reasonable fallback
  // strategy for (presumably less common) cases were we don't split things
  // correctly.
  //
  // Note also that the version splitting is only done for certain commonly-
  // used targets.
  //
  // Some examples of canonicalization and splitting:
  //
  // x86_64-apple-darwin14.5.0         x86_64  apple      darwin         14.5.0
  // x86_64-unknown-freebsd10.2        x86_64             freebsd        10.2
  // i686-elf                          i686               elf
  // arm-eabi                          arm                eabi
  // arm-none-eabi                     arm                eabi
  // arm-none-linux-gnueabi            arm                linux-gnueabi
  // arm-softfloat-linux-gnu           arm     softfloat  linux-gnu
  // i686-pc-mingw32                   i686               mingw32
  // i686-w64-mingw32                  i686    w64        mingw32
  // i686-lfs-linux-gnu                i686    lfs        linux-gnu
  // x86_64-unknown-linux-gnu          x86_64             linux-gnu
  // x86_64-linux-gnux32               x86_64             linux-gnux32
  // x86_64-microsoft-win32-msvc14.0   x86_64  microsoft  win32-msvc     14.0
  // x86_64-pc-windows-msvc            x86_64             windows-msvc
  // x86_64-pc-windows-msvc19.11.25547 x86_64             windows-msvc   19.11.25547
  //
  // Similar to version splitting, for certain commonly-used targets we also
  // derive the "target class" which can be used as a shorthand, more
  // convenient way to identify a targets. If the target is not recognized,
  // then the special 'other' value is used. Currently the following classes
  // are recognized:
  //
  // linux     *-*-linux-*
  // macos     *-apple-darwin*
  // bsd       *-*-(freebsd|openbsd|netbsd)*
  // windows   *-*-win32-* | *-*-windows-* | *-*-mingw32
  //
  // References:
  //
  // 1. The libtool repository contains the PLATFORM file that lists many known
  //    triplets.
  //
  // 2. LLVM has the Triple class with similar goals.
  //
  struct LIBBUTL_SYMEXPORT target_triplet
  {
    std::string cpu;
    std::string vendor;
    std::string system;
    std::string version;
    std::string class_;

    // Assemble and returning the canonical (i.e., the one we round-trip)
    // target triplet string.
    //
    std::string
    string () const;

    bool
    empty () const {return cpu.empty ();}

    int
    compare (const target_triplet& y) const
    {
      int r;
      return
        (r =    cpu.compare (y.cpu))    != 0 ? r :
        (r = vendor.compare (y.vendor)) != 0 ? r :
        (r = system.compare (y.system)) != 0 ? r :
        (   version.compare (y.version));
    }

    // Parse the triplet throw std::invalid_argument if the triplet is not
    // recognizable.
    //
    explicit
    target_triplet (const std::string&);

    target_triplet () {} // = default; @@ MOD VC
  };

  inline bool
  operator== (const target_triplet& x, const target_triplet& y)
  {
    return x.compare (y) == 0;
  }

  inline bool
  operator!= (const target_triplet& x, const target_triplet& y)
  {
    return !(x == y);
  }

  inline std::ostream&
  operator<< (std::ostream& o, const target_triplet& x)
  {
    return o << x.string ();
  }
}