#!/usr/bin/env python3
# Copyright 2025 The Emscripten Authors.  All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License.  Both these licenses can be
# found in the LICENSE file.

"""The functions in this file define the compiler flags that emcc passes to clang.

There are three different levels of flags, each one a superset of the next:

get_target_flags(): Defines just `-target` flags and should always be
used when calling clang, or any other llvm tool.

get_clang_flags(): In addition to the target flags this function returns all the
required compiler flags.

get_cflags(): In addition to compiler flags this function also returns pre-processor
flags. For example, include paths and macro definitions.
"""

import os

from . import building, cache, ports, shared, utils
from .cmdline import SIMD_INTEL_FEATURE_TOWER, SIMD_NEON_FLAGS
from .settings import settings
from .utils import memoize


def get_target_flags():
  return ['-target', shared.get_llvm_target()]


def get_clang_flags(user_args):
  flags = get_target_flags()

  # if exception catching is disabled, we can prevent that code from being
  # generated in the frontend
  if settings.DISABLE_EXCEPTION_CATCHING and not settings.WASM_EXCEPTIONS:
    flags.append('-fignore-exceptions')

  if settings.INLINING_LIMIT:
    flags.append('-fno-inline-functions')

  if settings.PTHREADS:
    if '-pthread' not in user_args:
      flags.append('-pthread')
  elif settings.SHARED_MEMORY:
    if '-matomics' not in user_args:
      flags.append('-matomics')
    if '-mbulk-memory' not in user_args:
      flags.append('-mbulk-memory')

  if (settings.MAIN_MODULE or settings.SIDE_MODULE) and '-fPIC' not in user_args:
    flags.append('-fPIC')

  if settings.MAIN_MODULE or settings.SIDE_MODULE or settings.LINKABLE or '-fPIC' in user_args:
    if not any(a.startswith('-fvisibility') for a in user_args):
      # For relocatable code we default to visibility=default in emscripten even
      # though the upstream backend defaults visibility=hidden.  This matches the
      # expectations of C/C++ code in the wild which expects undecorated symbols
      # to be exported to other DSO's by default.
      flags.append('-fvisibility=default')

  for a in building.llvm_backend_args():
    flags += ['-mllvm', a]

  return flags


@memoize
def get_cflags(user_args):
  # Flags we pass to the compiler when building C/C++ code
  # We add these to the user's flags (newargs), but not when building .s or .S assembly files
  cflags = get_clang_flags(user_args)
  cflags.append('--sysroot=' + cache.get_sysroot(absolute=True))

  if settings.EMSCRIPTEN_TRACING:
    cflags.append('-D__EMSCRIPTEN_TRACING__=1')

  if settings.SHARED_MEMORY:
    cflags.append('-D__EMSCRIPTEN_SHARED_MEMORY__=1')

  if settings.WASM_WORKERS:
    cflags.append('-D__EMSCRIPTEN_WASM_WORKERS__=1')

  ports.add_cflags(cflags, settings)

  # The rest of this function does a bunch of membership testing on user_args
  user_args = set(user_args)

  def user_args_contains_any(args):
    return any(a in user_args for a in args)

  if user_args_contains_any(SIMD_INTEL_FEATURE_TOWER) or user_args_contains_any(SIMD_NEON_FLAGS):
    if '-msimd128' not in user_args and '-mrelaxed-simd' not in user_args:
      utils.exit_with_error('passing any of ' + ', '.join(SIMD_INTEL_FEATURE_TOWER + SIMD_NEON_FLAGS) + ' flags also requires passing -msimd128 (or -mrelaxed-simd)!')
    cflags += ['-D__SSE__=1']

  if user_args_contains_any(SIMD_INTEL_FEATURE_TOWER[1:]):
    cflags += ['-D__SSE2__=1']

  if user_args_contains_any(SIMD_INTEL_FEATURE_TOWER[2:]):
    cflags += ['-D__SSE3__=1']

  if user_args_contains_any(SIMD_INTEL_FEATURE_TOWER[3:]):
    cflags += ['-D__SSSE3__=1']

  if user_args_contains_any(SIMD_INTEL_FEATURE_TOWER[4:]):
    cflags += ['-D__SSE4_1__=1']

  # Handle both -msse4.2 and its alias -msse4.
  if user_args_contains_any(SIMD_INTEL_FEATURE_TOWER[5:]):
    cflags += ['-D__SSE4_2__=1']

  if user_args_contains_any(SIMD_INTEL_FEATURE_TOWER[7:]):
    cflags += ['-D__AVX__=1']

  if user_args_contains_any(SIMD_INTEL_FEATURE_TOWER[8:]):
    cflags += ['-D__AVX2__=1']

  if user_args_contains_any(SIMD_INTEL_FEATURE_TOWER[9:]):
    cflags += ['-D__FMA__=1']

  if user_args_contains_any(SIMD_NEON_FLAGS):
    cflags += ['-D__ARM_NEON__=1']

  if '-nostdinc' not in user_args:
    if not settings.USE_SDL:
      cflags += ['-Xclang', '-iwithsysroot' + os.path.join('/include', 'fakesdl')]
    cflags += ['-Xclang', '-iwithsysroot' + os.path.join('/include', 'compat')]

  return cflags
