package config

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file cfg_ppx.ml

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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
open Ppxlib

let tag = "cfg"
let short_tag = "config"
let is_config_tag str = String.equal str tag || String.equal str short_tag

let user_env =
  Unix.environment () |> Array.to_list
  |> List.map (fun kv ->
         let[@warning "-8"] (k :: v) = String.split_on_char '=' kv in
         (k, String.concat "=" v))

let env =
  user_env
  @ [
      ("target_os", Cfg.target_os);
      ("target_arch", Cfg.target_arch);
      ("target_env", Cfg.target_env);
    ]
  |> List.sort_uniq (fun (k1, _) (k2, _) -> String.compare k1 k2)

let () =
  if Option.is_some (Sys.getenv_opt "CONFIG_DEBUG") then (
    Format.printf "Config PPX running with environment:\n\n%!";
    List.iter (fun (k, v) -> Format.printf "  %s = %S\r\n" k v) env;
    Format.printf "\n%!")

let env = List.map (fun (k, v) -> (k, Cfg_lang.Parser.String v)) env

let eval_attr attr =
  if not (is_config_tag attr.attr_name.txt) then `keep
  else
    let loc = attr.attr_loc in
    (* Printf.printf "\n\nattr name: %S\n\n" attr.attr_name.txt; *)
    match attr.attr_payload with
    | PStr [ { pstr_desc = Pstr_eval (e, []); _ } ] ->
        (* let e_ = Pprintast.string_of_expression e in *)
        (* Printf.printf "\n\npayload: %S\n\n" e_; *)
        if Cfg_lang.eval ~loc ~env e then `keep else `drop
    | _ -> `keep

let rec should_keep attrs =
  match attrs with
  | [] -> `keep
  | attr :: attrs -> if eval_attr attr = `drop then `drop else should_keep attrs

let rec should_keep_many list fn =
  match list with
  | [] -> `keep
  | item :: list ->
      if should_keep (fn item) = `drop then `drop else should_keep_many list fn

let apply_config_on_types (tds : type_declaration list) =
  List.filter_map
    (fun td ->
      match td with
      | {
       ptype_kind = Ptype_abstract;
       ptype_manifest =
         Some
           ({ ptyp_desc = Ptyp_variant (rows, closed_flag, labels); _ } as
            manifest);
       _;
      } ->
          let rows =
            List.filter_map
              (fun row ->
                if should_keep row.prf_attributes = `keep then Some row
                else None)
              rows
          in

          if rows = [] then None
          else
            Some
              {
                td with
                ptype_manifest =
                  Some
                    {
                      manifest with
                      ptyp_desc = Ptyp_variant (rows, closed_flag, labels);
                    };
              }
      | { ptype_kind = Ptype_variant cstrs; _ } ->
          let cstrs =
            List.filter_map
              (fun cstr ->
                if should_keep cstr.pcd_attributes = `keep then Some cstr
                else None)
              cstrs
          in

          if cstrs = [] then None
          else Some { td with ptype_kind = Ptype_variant cstrs }
      | { ptype_kind = Ptype_record labels; _ } ->
          let labels =
            List.filter_map
              (fun label ->
                if should_keep label.pld_attributes = `keep then Some label
                else None)
              labels
          in

          if labels = [] then None
          else Some { td with ptype_kind = Ptype_record labels }
      | _ -> Some td)
    tds

let apply_config_on_cases (cases : cases) =
  List.filter
    (fun case -> should_keep case.pc_rhs.pexp_attributes = `keep)
    cases

let rec apply_config_on_expression (exp : expression) =
  let pexp_desc =
    match exp.pexp_desc with
    | Pexp_try (exp, cases) ->
        let exp = apply_config_on_expression exp in
        let cases = apply_config_on_cases cases in
        Pexp_try (exp, cases)
    | Pexp_match (exp, cases) ->
        let exp = apply_config_on_expression exp in
        let cases = apply_config_on_cases cases in
        Pexp_match (exp, cases)
    | Pexp_fun (arg_label, exp_opt, pat, exp) ->
        let exp = apply_config_on_expression exp in
        Pexp_fun (arg_label, exp_opt, pat, exp)
    | Pexp_function cases ->
        let cases = apply_config_on_cases cases in
        Pexp_function cases
    | Pexp_let (rec_flag, vbs, exp) ->
        let exp = apply_config_on_expression exp in
        Pexp_let (rec_flag, vbs, exp)
    | _ -> exp.pexp_desc
  in
  { exp with pexp_desc }

let apply_config_on_value_bindings (vbs : value_binding list) =
  List.filter_map
    (fun vb ->
      if should_keep vb.pvb_attributes = `keep then
        Some { vb with pvb_expr = apply_config_on_expression vb.pvb_expr }
      else None)
    vbs

let apply_config_on_signature_items sig_items =
  List.filter_map
    (fun sig_item ->
      match sig_item.psig_desc with
      | Psig_value val_desc ->
          if should_keep val_desc.pval_attributes = `keep then Some sig_item
          else None
      | Psig_type (rec_flag, tds) ->
          let tds = apply_config_on_types tds in
          if List.length tds = 0 then None
          else Some { sig_item with psig_desc = Psig_type (rec_flag, tds) }
      | _ -> Some sig_item)
    sig_items

let apply_config_on_module_type mod_type =
  match mod_type.pmty_desc with
  | Pmty_signature signature_items ->
      let signature_items = apply_config_on_signature_items signature_items in
      { mod_type with pmty_desc = Pmty_signature signature_items }
  | _ -> mod_type

let rec apply_config_on_module_expr mod_expr =
  match mod_expr.pmod_desc with
  | Pmod_apply _ | Pmod_unpack _ | Pmod_extension _ | Pmod_ident _
  | Pmod_functor _ ->
      mod_expr
  | Pmod_structure structs ->
      let new_structs =
        List.filter_map
          (fun stri ->
            match stri.pstr_desc with
            | Pstr_value (rec_flag, vbs) ->
                let vbs = apply_config_on_value_bindings vbs in
                if List.length vbs = 0 then None
                else Some { stri with pstr_desc = Pstr_value (rec_flag, vbs) }
            | _ -> Some stri)
          structs
      in
      { mod_expr with pmod_desc = Pmod_structure new_structs }
  | Pmod_constraint (module_expr, module_type) ->
      let module_expr = apply_config_on_module_expr module_expr in
      let module_type = apply_config_on_module_type module_type in
      { mod_expr with pmod_desc = Pmod_constraint (module_expr, module_type) }

let apply_config_on_structure_item stri =
  try
    match stri.pstr_desc with
    | Pstr_typext { ptyext_attributes = attrs; _ }
    | Pstr_open { popen_attributes = attrs; _ }
    | Pstr_include { pincl_attributes = attrs; _ }
    | Pstr_exception { ptyexn_attributes = attrs; _ }
    | Pstr_primitive { pval_attributes = attrs; _ }
    | Pstr_eval (_, attrs) ->
        if should_keep attrs = `keep then Some stri else None
    | Pstr_modtype { pmtd_attributes; pmtd_name; pmtd_type; pmtd_loc } ->
        if should_keep pmtd_attributes = `keep then
          match pmtd_type with
          | None -> Some stri
          | Some pmtd_type ->
              let pmtd_type = Some (apply_config_on_module_type pmtd_type) in
              Some
                {
                  stri with
                  pstr_desc =
                    Pstr_modtype
                      { pmtd_attributes; pmtd_name; pmtd_type; pmtd_loc };
                }
        else None
    | Pstr_module { pmb_expr; pmb_name; pmb_attributes; pmb_loc } ->
        if should_keep pmb_attributes = `keep then
          let pmb_expr = apply_config_on_module_expr pmb_expr in
          Some
            {
              stri with
              pstr_desc =
                Pstr_module { pmb_expr; pmb_name; pmb_attributes; pmb_loc };
            }
        else None
    | Pstr_value (recflag, vbs) ->
        if should_keep_many vbs (fun vb -> vb.pvb_attributes) = `keep then
          let vbs = apply_config_on_value_bindings vbs in
          Some { stri with pstr_desc = Pstr_value (recflag, vbs) }
        else None
    | Pstr_type (recflag, tds) ->
        if should_keep_many tds (fun td -> td.ptype_attributes) = `keep then
          let tds = apply_config_on_types tds in
          Some { stri with pstr_desc = Pstr_type (recflag, tds) }
        else None
    | Pstr_recmodule md ->
        if should_keep_many md (fun md -> md.pmb_attributes) = `keep then
          Some stri
        else None
    | Pstr_class cds ->
        if should_keep_many cds (fun cd -> cd.pci_attributes) = `keep then
          Some stri
        else None
    | Pstr_class_type ctds ->
        if should_keep_many ctds (fun ctd -> ctd.pci_attributes) = `keep then
          Some stri
        else None
    | Pstr_extension _ | Pstr_attribute _ -> Some stri
  with Cfg_lang.Error { loc; error } ->
    let ext = Location.error_extensionf ~loc "%s" error in
    Some (Ast_builder.Default.pstr_extension ~loc ext [])

let apply_config_on_signature_item sigi =
  try
    match sigi.psig_desc with
    | Psig_typext { ptyext_attributes = attrs; _ }
    | Psig_modtype { pmtd_attributes = attrs; _ }
    | Psig_open { popen_attributes = attrs; _ }
    | Psig_include { pincl_attributes = attrs; _ }
    | Psig_exception { ptyexn_attributes = attrs; _ }
    | Psig_value { pval_attributes = attrs; _ }
    | Psig_modtypesubst { pmtd_attributes = attrs; _ }
    | Psig_modsubst { pms_attributes = attrs; _ }
    | Psig_module { pmd_attributes = attrs; _ } ->
        if should_keep attrs = `keep then Some sigi else None
    | Psig_typesubst tds ->
        if should_keep_many tds (fun td -> td.ptype_attributes) = `keep then
          let tds = apply_config_on_types tds in
          Some { sigi with psig_desc = Psig_typesubst tds }
        else None
    | Psig_type (recflag, tds) ->
        if should_keep_many tds (fun td -> td.ptype_attributes) = `keep then
          let tds = apply_config_on_types tds in
          Some { sigi with psig_desc = Psig_type (recflag, tds) }
        else None
    | Psig_recmodule md ->
        if should_keep_many md (fun md -> md.pmd_attributes) = `keep then
          Some sigi
        else None
    | Psig_class cds ->
        if should_keep_many cds (fun cd -> cd.pci_attributes) = `keep then
          Some sigi
        else None
    | Psig_class_type ctds ->
        if should_keep_many ctds (fun ctd -> ctd.pci_attributes) = `keep then
          Some sigi
        else None
    | Psig_extension _ | Psig_attribute _ -> Some sigi
  with Cfg_lang.Error { loc; error } ->
    let ext = Location.error_extensionf ~loc "%s" error in
    Some (Ast_builder.Default.psig_extension ~loc ext [])

let preprocess_impl str =
  match str with
  | { pstr_desc = Pstr_attribute attr; _ } :: rest
    when is_config_tag attr.attr_name.txt ->
      if eval_attr attr = `keep then rest else []
  | _ -> List.filter_map apply_config_on_structure_item str

let preprocess_intf sigi =
  match sigi with
  | { psig_desc = Psig_attribute attr; _ } :: rest
    when is_config_tag attr.attr_name.txt ->
      if eval_attr attr = `keep then rest else []
  | _ -> List.filter_map apply_config_on_signature_item sigi

let () =
  Driver.register_transformation tag ~aliases:[ short_tag ] ~preprocess_impl
    ~preprocess_intf
OCaml

Innovation. Community. Security.