How to automate compile_commands.json generation?

I would like to know any ways of doing this for c based on the c template nix flake from the nix-community

You can use bear to generate compile flags. It is just “bear” in Nixpkgs and can easily be added to ‘nativeBuildInputs’ in the flake.
bear -- cc foo.c will generate a compile_comands.json.

To automate the generation, you can replace the object linking in the makefile with something like bear --append -- $(CC) -c $(CFLAGS) $< -o $@

Let me know if you need any more help!

thanks
the idea of putting the call to bear in the makefile never occured to me
but when in the makefile bear isn’t working for me

Could you please go into more detail about the way it’s not working? On my system the following flake.nix and makefile based on the nix community flake properly generates a compile_commands.json that clangd is able to properly detect.

{
  description = "C Template";

  inputs = {
    nixpkgs.url = "nixpkgs";
    systems.url = "github:nix-systems/x86_64-linux";
    flake-utils = {
      url = "github:numtide/flake-utils";
      inputs.systems.follows = "systems";
    };
  };

  outputs =
    { self
    , nixpkgs
    , flake-utils
    , ...
    }:
    # For more information about the C/C++ infrastructure in nixpkgs: https://nixos.wiki/wiki/C
    flake-utils.lib.eachDefaultSystem (system:
    let
      pkgs = nixpkgs.legacyPackages.${system};
      pname = "hello-world"; #package name
      version = "0.0.1";
      src = ./.;
      buildInputs = with pkgs; [
        # add library dependencies here i.e.
        #zlib
        # Tipp: you can use `nix-locate foo.h` to find the package that provides a header file, see https://github.com/nix-community/nix-index
      ];
      nativeBuildInputs = with pkgs; [
        # add build dependencies here
        ## For mesonbuild:
        #meson ninja
        ## For cmake:
        #cmake
        ## For autotools:
        # autoconf-archive
        # autoreconfHook
        pkg-config
        # clangd language server.
        # Also start your IDE/editor from the shell provided by `nix develop` as the wrapped clangd from clang-tools needs environment variables set by the shell
        clang-tools
        bear
      ];
    in
    {
      devShells.default = pkgs.mkShell {
        inherit buildInputs nativeBuildInputs;

        # You can use NIX_CFLAGS_COMPILE to set the default CFLAGS for the shell
        #NIX_CFLAGS_COMPILE = "-g";
        # You can use NIX_LDFLAGS to set the default linker flags for the shell
        #NIX_LDFLAGS = "-L${lib.getLib zstd}/lib -lzstd";
      };

      # Pinned gcc: remain on gcc10 even after `nix flake update`
      #default = pkgs.mkShell.override { stdenv = pkgs.gcc10Stdenv; } {
      #  inherit buildInputs nativeBuildInputs;
      #};

      # Clang example:
      #default = pkgs.mkShell.override { stdenv = pkgs.clangStdenv; } {
      #  inherit buildInputs nativeBuildInputs;
      #};

      packages.default = pkgs.stdenv.mkDerivation {
        inherit buildInputs nativeBuildInputs pname version src;
      };
    });
}
# The name of the source files
SOURCES = main.c

pname = result

# Flags for compilation (adding warnings are always good)
CFLAGS = -Wall

# Flags for linking (none for the moment)
LDFLAGS =

# Libraries to link with (none for the moment)
LIBS =

# This creates a list of object files from the source files
OBJECTS = $(SOURCES:%.c=%.o)

# The first target, this will be the default target if none is specified
# This target tells "make" to make the "all" target
default: all

# Having an "all" target is customary, so one could write "make all"
# It depends on the executable program
all: $(pname)

# This will link the executable from the object files
$(pname): $(OBJECTS)
	$(CC) $(LDFLAGS) $(OBJECTS) -o $(pname) $(LIBS)

# This is a target that will compile all needed source files into object files
# We don't need to specify a command or any rules, "make" will handle it automatically
%.o: %.c
	bear --append -- $(CC) -c $(CFLAGS) $< -o $@

# Target to clean up after us
clean:
	-rm -f $(pname)      # Remove the executable file
	-rm -f $(OBJECTS)  # Remove the object files

install: $(pname)
	mkdir -p $(out)/bin
	install -m 755 $(pname) $(out)/bin/$(pname)

main.o: main.c

the compile_commands.json doesn’t get updated when i run nix build or nix run
(edit) i have copied your makefile and flake.nix into a blank project and it didn’t update the compile_commands.json
(edit 2) i have checked and bear does get called

i got a compile_commands.json generating but it only goes as deep as the wrapper but needs to go one level deeper
i did it with a makefile that looks like this

# The name of the source files
SOURCES = main.c

# Flags for compilation (adding warnings are always good)
CFLAGS = -Wall

# Flags for linking (none for the moment)
LDFLAGS =

# Libraries to link with (none for the moment)
LIBS =

# This creates a list of object files from the source files
OBJECTS = $(SOURCES:%.c=%.o)

# The first target, this will be the default target if none is specified
# This target tells "make" to make the "all" target
default: all

# Having an "all" target is customary, so one could write "make all"
# It depends on the executable program
all: $(pname)

# This will link the executable from the object files
$(pname): $(OBJECTS)
	$(CC) $(LDFLAGS) $(OBJECTS) -o $(pname) $(LIBS)

# This is a target that will compile all needed source files into object files
# We don't need to specify a command or any rules, "make" will handle it automatically
%.o: %.c
	bear --append -- $(CC) -c $(CFLAGS) $< -o $@

# Target to clean up after us
clean:
	-rm -f $(pname)      # Remove the executable file
	-rm -f $(OBJECTS)  # Remove the object files

install: $(pname)
	mkdir -p $(out)/bin
	install -m 755 compile_commands.json $(out)/compile_commands.json
	install $(pname) $(out)/bin/$(pname)

main.o: main.c


and then creating a symlink to the compile commands.json throught the symlink to the result

my compile_commands.json looks like

[
  {
    "arguments": [
      "/nix/store/vh9fsdhgxcnab2qk7vdp2palkkn6j3cp-gcc-wrapper-13.3.0/bin/gcc",
      "-c",
      "-Wall",
      "-I./include/",
      "-o",
      "test.o",
      "test.c"
    ],
    "directory": "/build/scymb53ycqa5n8h5jxi7vw95qva4ixdc-source",
    "file": "/build/scymb53ycqa5n8h5jxi7vw95qva4ixdc-source/test.c",
    "output": "/build/scymb53ycqa5n8h5jxi7vw95qva4ixdc-source/test.o"
  },
  {
    "arguments": [
      "/nix/store/vh9fsdhgxcnab2qk7vdp2palkkn6j3cp-gcc-wrapper-13.3.0/bin/gcc",
      "-c",
      "-Wall",
      "-I./include/",
      "-o",
      "main.o",
      "main.c"
    ],
    "directory": "/build/scymb53ycqa5n8h5jxi7vw95qva4ixdc-source",
    "file": "/build/scymb53ycqa5n8h5jxi7vw95qva4ixdc-source/main.c",
    "output": "/build/scymb53ycqa5n8h5jxi7vw95qva4ixdc-source/main.o"
  }
]

this abomination works

NIX_DEBUG=1 clang main.c 2>&1 | head -n $(NIX_DEBUG=1 clang main.c 2>&1 | grep -n -m 1 '/bin/ld' | sed 's/\([0-9]*\).*/\1/') | grep '  ' | sed 's/^  \(.*\)$/\1/' > compile_flags.txt