tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

build.rs (9803B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 extern crate cc;
      6 extern crate glsl_to_cxx;
      7 extern crate webrender_build;
      8 
      9 use std::collections::HashSet;
     10 use std::fmt::Write;
     11 use webrender_build::shader::{get_shader_features, ShaderFeatureFlags};
     12 
     13 // Shader key is in "name feature,feature" format.
     14 // File name needs to be formatted as "name_feature_feature".
     15 fn shader_file(shader_key: &str) -> String {
     16    shader_key.replace([' ', ','], "_")
     17 }
     18 
     19 fn write_load_shader(shader_keys: &[String]) {
     20    let mut load_shader = String::new();
     21    for s in shader_keys {
     22        let _ = writeln!(load_shader, "#include \"{}.h\"", shader_file(s));
     23    }
     24    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
     25    for s in shader_keys {
     26        let _ = writeln!(
     27            load_shader,
     28            "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}",
     29            s,
     30            shader_file(s)
     31        );
     32    }
     33    load_shader.push_str("  return nullptr;\n}\n");
     34    std::fs::write(
     35        std::env::var("OUT_DIR").unwrap() + "/load_shader.h",
     36        load_shader,
     37    )
     38    .unwrap();
     39 }
     40 
     41 fn process_imports(
     42    shader_dir: &str,
     43    shader: &str,
     44    included: &mut HashSet<String>,
     45    output: &mut String,
     46 ) {
     47    if !included.insert(shader.into()) {
     48        return;
     49    }
     50    println!("cargo:rerun-if-changed={shader_dir}/{shader}.glsl");
     51    let source = std::fs::read_to_string(format!("{shader_dir}/{shader}.glsl")).unwrap();
     52    for line in source.lines() {
     53        if let Some(imports) = line.strip_prefix("#include ") {
     54            let imports = imports.split(',');
     55            for import in imports {
     56                process_imports(shader_dir, import, included, output);
     57            }
     58        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
     59            // ignore
     60        } else {
     61            output.push_str(line);
     62            output.push('\n');
     63        }
     64    }
     65 }
     66 
     67 fn translate_shader(
     68    shader_key: &str,
     69    shader_dir: &str,
     70    suppressed_env_vars: &mut Option<Vec<EnvVarGuard>>,
     71 ) {
     72    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
     73    let _ = writeln!(
     74        imported,
     75        "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U",
     76        webrender_build::MAX_VERTEX_TEXTURE_WIDTH
     77    );
     78 
     79    let (basename, features) =
     80        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
     81    if !features.is_empty() {
     82        for feature in features.trim().split(',') {
     83            let _ = writeln!(imported, "#define WR_FEATURE_{feature}");
     84        }
     85    }
     86 
     87    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);
     88 
     89    let shader = shader_file(shader_key);
     90 
     91    let out_dir = std::env::var("OUT_DIR").unwrap();
     92    let imp_name = format!("{out_dir}/{shader}.c");
     93    std::fs::write(&imp_name, imported).unwrap();
     94 
     95    // We need to ensure that the C preprocessor does not pull compiler flags from the host or
     96    // target environment. Set all `CFLAGS` or `CXXFLAGS` env. vars. to empty to work around this.
     97    let _ = suppressed_env_vars.get_or_insert_with(|| {
     98        cflags_env_vars()
     99            .map(|(key, value)| {
    100                std::env::set_var(&key, "");
    101                EnvVarGuard {
    102                    key,
    103                    old_value: Some(value),
    104                }
    105            })
    106            .collect::<Vec<_>>()
    107    });
    108 
    109    let mut build = cc::Build::new();
    110    build.no_default_flags(true);
    111    if let Ok(tool) = build.try_get_compiler() {
    112        if tool.is_like_msvc() {
    113            build.flag("/EP");
    114            if tool.path().to_str().is_some_and(|p| p.contains("clang")) {
    115                build.flag("/clang:-undef");
    116            } else {
    117                build.flag("/u");
    118            }
    119        } else {
    120            build.flag("-xc").flag("-P").flag("-undef");
    121        }
    122    }
    123    build.file(&imp_name);
    124    let vs = build.clone().define("WR_VERTEX_SHADER", Some("1")).expand();
    125    let fs = build
    126        .clone()
    127        .define("WR_FRAGMENT_SHADER", Some("1"))
    128        .expand();
    129    let vs_name = format!("{out_dir}/{shader}.vert");
    130    let fs_name = format!("{out_dir}/{shader}.frag");
    131    std::fs::write(&vs_name, vs).unwrap();
    132    std::fs::write(&fs_name, fs).unwrap();
    133 
    134    let args = vec!["glsl_to_cxx".to_string(), vs_name, fs_name];
    135    let result = glsl_to_cxx::translate(&mut args.into_iter());
    136    std::fs::write(format!("{out_dir}/{shader}.h"), result).unwrap();
    137 }
    138 
    139 fn main() {
    140    let shader_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res";
    141 
    142    let shader_flags = ShaderFeatureFlags::GL
    143        | ShaderFeatureFlags::DUAL_SOURCE_BLENDING
    144        | ShaderFeatureFlags::ADVANCED_BLEND_EQUATION
    145        | ShaderFeatureFlags::DEBUG;
    146    let mut shaders: Vec<String> = Vec::new();
    147    for (name, features) in get_shader_features(shader_flags) {
    148        shaders.extend(features.iter().map(|f| {
    149            if f.is_empty() {
    150                name.to_owned()
    151            } else {
    152                format!("{name} {f}")
    153            }
    154        }));
    155    }
    156 
    157    shaders.sort();
    158 
    159    let mut suppressed_env_vars = None;
    160    for shader in &shaders {
    161        translate_shader(shader, &shader_dir, &mut suppressed_env_vars);
    162    }
    163    drop(suppressed_env_vars); // Restore env. vars. for further compilation.
    164 
    165    write_load_shader(&shaders);
    166 
    167    println!("cargo:rerun-if-changed=src/blend.h");
    168    println!("cargo:rerun-if-changed=src/composite.h");
    169    println!("cargo:rerun-if-changed=src/gl_defs.h");
    170    println!("cargo:rerun-if-changed=src/glsl.h");
    171    println!("cargo:rerun-if-changed=src/program.h");
    172    println!("cargo:rerun-if-changed=src/rasterize.h");
    173    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    174    println!("cargo:rerun-if-changed=src/texture.h");
    175    println!("cargo:rerun-if-changed=src/vector_type.h");
    176    println!("cargo:rerun-if-changed=src/gl.cc");
    177    let mut build = cc::Build::new();
    178    build.cpp(true);
    179 
    180    let _suppressed_cflags_env_vars = cflags_env_vars()
    181        .filter_map(|(key, value)| {
    182            // NOTE: Cancels out [the `moz-check` plugin added in
    183            // `build/moz.configure/clang_plugin.configure`'s `clang_plugin_flags`
    184            // assignment][crossref].
    185            //
    186            // [crossref]: https://searchfox.org/mozilla-central/rev/f008b9aa2adf2dca6bdd49855b314cb3195f6f27/build/moz.configure/clang_plugin.configure#77-80
    187            const MOZ_CHECK_PLUGIN_LOAD_ARGS: &str = "-Xclang -add-plugin -Xclang moz-check";
    188 
    189            let replaced = value
    190                .to_str()
    191                .filter(|v| v.contains(MOZ_CHECK_PLUGIN_LOAD_ARGS))?
    192                .replace(MOZ_CHECK_PLUGIN_LOAD_ARGS, "");
    193 
    194            std::env::set_var(&key, replaced);
    195            Some(EnvVarGuard {
    196                key,
    197                old_value: Some(value),
    198            })
    199        })
    200        .collect::<Vec<_>>();
    201 
    202    if let Ok(tool) = build.try_get_compiler() {
    203        if tool.is_like_msvc() {
    204            build
    205                .flag("/std:c++20")
    206                .flag("/EHs-")
    207                .flag("/GR-");
    208        } else {
    209            build
    210                .flag("-std=c++20")
    211                .flag("-fno-exceptions")
    212                .flag("-fno-rtti")
    213                .flag("-fno-math-errno");
    214        }
    215        // SWGL relies heavily on inlining for performance so override -Oz with -O2
    216        if tool.args().contains(&"-Oz".into()) {
    217            build.flag("-O2");
    218        }
    219 
    220        // Most GLSL compilers assume something like fast-math so we turn it on.
    221        // However, reciprocal division makes it so 1/1 = 0.999994 which can produce a lot of fuzz
    222        // in reftests and the use of reciprocal instructions usually involves a refinement step
    223        // which bloats our already bloated code. Further, our shader code is sufficiently parallel
    224        // that we're more likely to be throughput bound vs latency bound. Having fewer
    225        // instructions makes things easier on the processor and in places where it matters we can
    226        // probably explicitly use reciprocal instructions and avoid the refinement step.
    227        // Also, allow checks for non-finite values which fast-math may disable.
    228        if tool.is_like_msvc() {
    229            build
    230                .flag("/fp:fast")
    231                .flag("-Xclang")
    232                .flag("-mrecip=none")
    233                .flag("/clang:-fno-finite-math-only");
    234        } else if tool.is_like_clang() {
    235            // gcc only supports -mrecip=none on some targets so to keep
    236            // things simple we don't use -ffast-math with gcc at all
    237            build
    238                .flag("-ffast-math")
    239                .flag("-mrecip=none")
    240                .flag("-fno-finite-math-only");
    241        }
    242    }
    243 
    244    build
    245        .file("src/gl.cc")
    246        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
    247        .include(shader_dir)
    248        .include("src")
    249        .include(std::env::var("OUT_DIR").unwrap())
    250        .compile("gl_cc");
    251 }
    252 
    253 struct EnvVarGuard {
    254    key: std::ffi::OsString,
    255    old_value: Option<std::ffi::OsString>,
    256 }
    257 
    258 impl Drop for EnvVarGuard {
    259    fn drop(&mut self) {
    260        let Self { key, old_value } = &*self;
    261        if let Some(old_value) = old_value.as_ref() {
    262            std::env::set_var(key, old_value);
    263        } else {
    264            std::env::remove_var(key);
    265        }
    266    }
    267 }
    268 
    269 fn cflags_env_vars() -> impl Iterator<Item = (std::ffi::OsString, std::ffi::OsString)> {
    270    std::env::vars_os().filter(|(key, _value)| {
    271        key.to_str().is_some_and(|key_utf8| {
    272            ["CFLAGS", "CXXFLAGS"]
    273                .iter()
    274                .any(|opt_name| key_utf8.contains(opt_name))
    275        })
    276    })
    277 }