A language 'L' is self hosted when it's implementation is written in 'L'.
To do this you need two things:
- The 'L'-compiler source code written in 'L'
- A compiled binary of the compiler
This can be achieved in a variety of ways.
The compiler source code is written. Then by hand a compiled version is thought out and typed out carefully. After that, the source code can be compiled by machine with the hand written binary to produce the compiled binary.
Algol 50 NELIAC and Knuth's WEB were implemented this way.
Interpreter then Compiler
The first LISP (1962) compiler was done this way. They had an interpreter for the language and then wrote the compiler in itself, compiled it by running it with the interpreter to get it built.
Another approach is to implement the compiler for 'L' in a similar pre-existing language 'K', then implement a transpiler from 'K' to 'L'. Now you can transpile the compiler to get the self hosted source which can be recompiled with the old compiler to get the newly compiled binary.
This is the approach golang used. D also applied this technique to the frontend of their compiler.