Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Inner Attributes makes it impossible to include code generation from build script. #306

Closed
MinisculeGirraffe opened this issue Apr 8, 2023 · 3 comments · Fixed by #309

Comments

@MinisculeGirraffe
Copy link

MinisculeGirraffe commented Apr 8, 2023

Howdy.

I was working on testing running code-generation in a build script, which I feel is preferable for multiple reasons. You can have the code automatically re-run at build time every time a file in the /prisma folder changes. With the benefit of it not being included in your actual /src folder so there's no way you can git commit or modify the file that gets code-generated.

I've got two crates in my workspace:

  • prisma-cli: The standard CLI setup from the docs.
  • prisma-codegen: A lib crate containing the /prisma/schema.prisma file and a build.rs script.

In the build script, we're executing the prisma-cli command to codegen, and setting an env variable to change the output of the generated file to be in the /target directory.

build.rs

use std::{env, path::Path, process::Command};

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let codegen_dest_path = Path::new(&out_dir).join("generated.rs");
    env::set_var("PRISMA_OUT_FILE", codegen_dest_path);
    env::set_var("CARGO_TARGET_DIR", &out_dir);
    let output = Command::new("cargo")
        .args(&["run", "-p", "prisma-cli", "generate"])
        .output()
        .expect("Failed run to codegen command");

    println!("cargo:rerun-if-changed=/prisma");
}

schema.prisma

generator client {
    // Corresponds to the cargo alias created earlier
    provider      = "cargo prisma"
    // The location to generate the client. Is relative to the position of the schema
    output        =  env("PRISMA_OUT_FILE")
}

lib.rs

include!(concat!(env!("OUT_DIR"), "/generated.rs"));

This all works. The problem is that the generated code uses #![allow(warnings, unused)] which isn't allowed when using an include! to bring the generated code into our libraries context. Compiler error for reference:

error: an inner attribute is not permitted in this context
 --> /Users/dnorred/app/back-end/target/debug/build/prisma-codegen-dd61d46b0c3f1ab5/out/generated.rs:3:1
  |
3 |   #![allow(warnings, unused)]
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 | / pub static DATAMODEL_STR: &'static str =
5 | |     include_str!("/Users/dnorred/app/back-end/prisma-codegen/prisma/schema.prisma");
  | |____________________________________________________________________________________- the inner attribute doesn't annotate this static item
  |
  = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
help: to annotate the static item, change the attribute from inner to outer style
  |
3 - #![allow(warnings, unused)]
3 + #[allow(warnings, unused)]
  |

Seems like this issue has happened with other projects that use code generation as well

google/flatbuffers#6261

I think the fix would be to just replace it.

@MinisculeGirraffe
Copy link
Author

This might sound dumb, but I just replaced that line in the build script with the recommendation from the compiler error message and that fixed it.

I'm able to use the "empty" prisma-codegen crate without having to write any files to /src, and the contents automatically get re-generated whenever I modify the schema file.

main.rs from the application workspace crate:

use prisma_codegen::{new_client_with_url, PrismaClient};
use color_eyre::Result;

/// Create a new prisma client, and apply any pending migrations
async fn prisma_client(url: &str) -> Result<PrismaClient> {
    let client = new_client_with_url(url).await?;

    #[cfg(debug_assertions)]
    client._db_push().await?;
    #[cfg(not(debug_assertions))]
    client._migrate_deploy().await?;
    Ok(client)
}

build.rs after modifications:

use std::io::{BufRead, BufReader};
use std::{env, path::Path, process::Command};
use std::{fs, fs::File};

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let codegen_dest_path = Path::new(&out_dir).join("generated.rs");
    env::set_var("PRISMA_OUT_FILE", &codegen_dest_path);
    env::set_var("CARGO_TARGET_DIR", &out_dir);
    let output = Command::new("cargo")
        .args(&["run", "-p", "prisma-cli", "generate"])
        .output()
        .expect("Failed run to codegen command");

    let mut lines = lines_from_file(&codegen_dest_path);

    for line in &mut lines {
        if line.contains("#!") {
            *line = line.replace("#!", "#");
        }
    }

    fs::write(&codegen_dest_path, lines.join("\n")).expect("Failed to update file");
    println!("cargo:rerun-if-changed=/prisma");
}

fn lines_from_file(filename: impl AsRef<Path>) -> Vec<String> {
    let file = File::open(filename).expect("no such file");
    let buf = BufReader::new(file);
    buf.lines()
        .map(|l| l.expect("Could not parse line"))
        .collect()
}

@Brendonovich
Copy link
Owner

Huh, never knew that include! worked like that. I also never knew that if you put #[allow(...)] on a mod ...; declaration it'll do the same as the inner attributes I'm currently using.
I think I'm going to get rid of them all together and recommend that users add the attributes themselves, this way you can either add them on the mod ...; or inject them at the top of the file in your build script kind of like the text replacement you're already doing.

@Brendonovich Brendonovich linked a pull request Apr 10, 2023 that will close this issue
@MinisculeGirraffe
Copy link
Author

FWIW I've had absolutely zero issues with the build script since then. I think it's the preferable way to go since every cargo-check will make sure you've got the latest codegen from the prisma schema. One less thing to worry about being fragile.

Appreciate the change. Hopefully I can remove that extra bit from the build script 😅.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants