header-filesvalavapi

Valac missing generated header


I have two files, client.vala and lib.vapi.
The VAPI defines a class (that would usually talk to C code):

class Toplevel.Sub.CClass
{
    public uint i;
}

And client.vala uses the class:

class Toplevel.Sub.UserClass
{
    public Toplevel.Sub.CClass c_class;
}

int main()
{
    var cls = new Toplevel.Sub.UserClass();
    cls.c_class.i = 0;
    return 0;
}

When I attempt to compile the program, I get an error:

$ valac client.vala lib.vapi
/tmp/bug/client.vala.c:7:20: fatal error: client.h: No such file or directory
compilation terminated.
error: cc exited with status 256
Compilation failed: 1 error(s), 0 warning(s)

The compiler seems to want a header generated for the functions in client.vala.

Is this a bug or am I missing something?


Solution

  • You've discovered something I didn't know about the Vala compiler. client.h is the default C header name generated by the compiler when no alternative is provided. It has used the basename of the source Vala file. You can change this using the cheader_filename CCode detail. Change what you have in your VAPI to:

    [CCode (cheader_filename = "my_real_c_header.h")]
    class Toplevel.Sub.CClass
    {
        public uint i;
    }
    

    and you will see the include is now #include "my_real_c_header.h"

    By the way if you compile with valac client.vala --vapididr . --pkg lib you will see the header is included as #include <my_real_c_header.h>. Personally I think VAPIs should be used with the --pkg option.

    If you are literally using Toplevel.Sub.CClass to 'usually talk to C code' then you have misunderstood the purpose of a VAPI. I take 'talk to' to mean calling various C functions and collecting the results so they can be presented in a more Vala friendly way to the rest of your program. So it is a wrapper interface.

    A VAPI contains instructions to the Vala compiler to translate names into the right names for the C interface. For example you might have a very simple C function, void top_level_do_something (), that you want to use in Vala. In Vala it helps to use a namespace so the VAPI could be:

    [CCode (cheader_filename = "my_real_c_header.h")]
    namespace TopLevel {
        [CCode (cname = "top_level_do_something")]
        public void do_something ();
    }
    

    You can then call this in Vala with TopLevel.do_something () and the compiler would write this out as top_level_do_something () in C.

    Take a look at Writing a VAPI Manually for more details.