Preprocessor

The #define, #ifdef and #version Preprocessor Directives

A C-style preprocessor rewrites the source before compilation; each directive starts its own line. #version 300 es must come first. #define and #undef manage macros; #if, #ifdef, #ifndef, #elif, #else and #endif include or skip code; #error stops with a message; #extension name : enable turns on an extension before any code; #pragma and #line pass hints and renumber lines. The predefined macros are __LINE__, __VERSION__, GL_ES and GL_FRAGMENT_PRECISION_HIGH. There is no #include:

Predefined macros, conditional compilation and a missing directiveJavaScript
show('result = vec4(__VERSION__, __LINE__, GL_ES, GL_FRAGMENT_PRECISION_HIGH);');
show('\n#if __VERSION__ >= 300\n result = vec4(3.0);\n#else\n result = vec4(1.0);\n#endif\n');
show('\n#include "lighting.glsl"\nresult = vec4(0.0);');
Output
300, 3, 1, 1
3, 3, 3, 3
ERROR: 0:4: 'include' : invalid directive name

The snippets begin with \n because GLH.evaluate() places the body right after void main() {, and a directive must start a line. Spellings from older tutorials, #undefine and #version 101, do not exist, and ANGLE 2,592 rejects both.

One cover shader compiled three ways with #define and #ifdef, plus the predefined macrosHTMLLive
<!doctype html>
<style>
  body { margin: 0; font: 12px system-ui, sans-serif; background: #f7f4ee; color: #333; }
  canvas { display: block; width: 100%; max-width: 600px; }
  .names { display: flex; max-width: 600px; text-align: center; font-family: monospace; font-size: 11px; }
  .names div { flex: 1; padding: 4px 2px; }
  pre { max-width: 600px; margin: 4px 0 0; padding: 6px 8px; background: #fff; border: 1px solid #ddd; font-size: 11px; white-space: pre-wrap; }
</style>
<canvas id="view" width="600" height="200"></canvas>
<div class="names"><div>(no defines)</div><div>#define SPINE</div><div>#define SPINE + #define GLOSS 0.6</div></div>
<pre id="out"></pre>
<script>
const gl = document.getElementById('view').getContext('webgl2');
const vs = `#version 300 es
void main() { gl_Position = vec4(gl_VertexID == 1 ? 3.0 : -1.0, gl_VertexID == 2 ? 3.0 : -1.0, 0.0, 1.0); }`;
// The shared body; #version must stay the very first line, so defines go after it.
const coverBody = `
precision mediump float;
uniform vec2 uOrigin;
out vec4 outColor;
void main() {
  vec2 uv = (gl_FragCoord.xy - uOrigin) / vec2(200.0);
  vec3 color = vec3(0.71, 0.27, 0.18);            // Salt and Saffron's cover
#ifdef SPINE
  color *= mix(0.55, 1.0, smoothstep(0.24, 0.27, uv.x));
#endif
#if defined(GLOSS) && __VERSION__ >= 300
  color += GLOSS * pow(max(0.0, 1.0 - abs(uv.x + uv.y - 1.1) * 4.0), 3.0);
#endif
  outColor = vec4(color, 1.0);
}`;
function compile(type, src) {
  const s = gl.createShader(type);
  gl.shaderSource(s, src); gl.compileShader(s);
  if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
  return s;
}
function variant(defines) {
  const p = gl.createProgram();
  gl.attachShader(p, compile(gl.VERTEX_SHADER, vs));
  gl.attachShader(p, compile(gl.FRAGMENT_SHADER, '#version 300 es\n' + defines + coverBody));
  gl.linkProgram(p);
  return p;
}
gl.bindVertexArray(gl.createVertexArray());
gl.enable(gl.SCISSOR_TEST);
['', '#define SPINE\n', '#define SPINE\n#define GLOSS 0.6\n'].forEach((defines, i) => {
  const p = variant(defines);
  gl.useProgram(p);
  gl.uniform2f(gl.getUniformLocation(p, 'uOrigin'), i * 200, 0);
  gl.scissor(i * 200 + 30, 10, 140, 180);         // one cover per variant
  gl.drawArrays(gl.TRIANGLES, 0, 3);
});

// Read the predefined macros back: render them into a 1x1 float target and readPixels.
gl.disable(gl.SCISSOR_TEST);
gl.getExtension('EXT_color_buffer_float');
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA32F, 1, 1);
gl.bindFramebuffer(gl.FRAMEBUFFER, gl.createFramebuffer());
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
gl.viewport(0, 0, 1, 1);
function evaluate(body) {
  try {
    const p = gl.createProgram();
    gl.attachShader(p, compile(gl.VERTEX_SHADER, vs));
    gl.attachShader(p, compile(gl.FRAGMENT_SHADER,
      `#version 300 es\nprecision highp float; out vec4 result;\nvoid main() {\n${body}\n}`));
    gl.linkProgram(p); gl.useProgram(p);
    gl.drawArrays(gl.TRIANGLES, 0, 3);
    const v = new Float32Array(4);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, v);
    return v.join(', ');
  } catch (e) { return e.message.split('\n')[0]; }
}
document.getElementById('out').textContent = [
  'vec4(__VERSION__, __LINE__, GL_ES, GL_FRAGMENT_PRECISION_HIGH) -> ' +
    evaluate('result = vec4(__VERSION__, __LINE__, GL_ES, GL_FRAGMENT_PRECISION_HIGH);'),
  '#if __VERSION__ >= 300 ... #else ... #endif -> ' +
    evaluate('#if __VERSION__ >= 300\n result = vec4(3.0);\n#else\n result = vec4(1.0);\n#endif'),
  '#include "lighting.glsl" -> ' + evaluate('#include "lighting.glsl"\nresult = vec4(0.0);'),
].join('\n');
</script>