text: # Copyright 2014-present PlatformIO <contact@platformio.org>
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #    http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  import sys
  from platform import system
  from os import makedirs
  from os.path import isdir, join
  
  from SCons.Script import (COMMAND_LINE_TARGETS, AlwaysBuild, Builder, Default,
                            DefaultEnvironment)
  
  env = DefaultEnvironment()
  platform = env.PioPlatform()
  
  framework_version = ''.join(['{0:0=3d}'.format(int(x)) for x in platform.get_package_version('framework-arduino-lpc176x').split('.')]) + 'UL'
  platform_version = ''.join(['{0:0=3d}'.format(int(x)) for x in platform.version.split('.')]) + 'UL'
  
  if "BOARD" in env:
      target_mcu = env.BoardConfig().get("build.mcu").upper()
  
  env.Replace(
      AR="arm-none-eabi-ar",
      AS="arm-none-eabi-as",
      CC="arm-none-eabi-gcc",
      CXX="arm-none-eabi-g++",
      GDB="arm-none-eabi-gdb",
      OBJCOPY="arm-none-eabi-objcopy",
      RANLIB="arm-none-eabi-ranlib",
      SIZETOOL="arm-none-eabi-size",
  
      ARFLAGS=["rc"],
  
      ASFLAGS=["-x", "assembler-with-cpp"],
  
      CCFLAGS=[
          "-Os",
          "-ffunction-sections",
          "-fdata-sections",
          "-fsingle-precision-constant",
          #"-ffast-math",
          #"-funsafe-math-optimizations",
          "-Wall",
          "-mthumb",
          "--specs=nano.specs",
          "--specs=nosys.specs",
          #"-flto",
          #"-ffat-lto-objects"
      ],
  
      CXXFLAGS=[
          "-std=gnu++17",
          "-fno-rtti",
          "-fno-exceptions",
          "-fno-use-cxa-atexit",
          "-fno-common",
          "-fno-threadsafe-statics"
      ],
  
      CPPDEFINES=[
          ("LPC176X_FRAMEWORK"),
          ("TARGET_LPC1768"),
          ("F_CPU", "$BOARD_F_CPU"),
          ("MCU_" + target_mcu),
          ("PIO_PLATFORM_VERSION", platform_version),
          ("PIO_FRAMEWORK_VERSION", framework_version)
      ],
  
      LINKFLAGS=[
          "-mthumb",
          "--specs=nano.specs",
          "--specs=nosys.specs",
          #"-u_printf_float",
          "-flto"
      ],
  
      LIBS=["c", "gcc", "m"],
  
      SIZEPROGREGEXP=r"^(?:\.text|\.ramcode|\.data|\.rodata|\.text.align|\.ARM.exidx)\s+(\d+).*",
      SIZEDATAREGEXP=r"^(?:\.ram_isr_vector|\.ramcode|\.data|\.bss|\.noinit)\s+(\d+).*",
      SIZECHECKCMD="$SIZETOOL -A -d $SOURCES",
      SIZEPRINTCMD='$SIZETOOL -B -d $SOURCES',
  
      PROGSUFFIX=".elf"
  )
  
  # Allow user to override via pre:script
  if env.get("PROGNAME", "program") == "program":
      env.Replace(PROGNAME="firmware")
  
  if "BOARD" in env:
      env.Append(
          CCFLAGS=[
              "-mcpu=%s" % env.BoardConfig().get("build.cpu")
          ],
          LINKFLAGS=[
              "-mcpu=%s" % env.BoardConfig().get("build.cpu")
          ]
      )
  
  env.Append(
      ASFLAGS=env.get("CCFLAGS", [])[:],
  
      BUILDERS=dict(
          ElfToBin=Builder(
              action=env.VerboseAction(" ".join([
                  "$OBJCOPY",
                  "-O",
                  "binary",
                  "$SOURCES",
                  "$TARGET"
              ]), "Building $TARGET"),
              suffix=".bin"
          ),
          ElfToHex=Builder(
              action=env.VerboseAction(" ".join([
                  "$OBJCOPY",
                  "-O",
                  "ihex",
                  "-R",
                  ".eeprom",
                  "$SOURCES",
                  "$TARGET"
              ]), "Building $TARGET"),
              suffix=".hex"
          )
      )
  )
  
  #
  # Target: Build executable and linkable firmware
  #
  
  target_elf = None
  if "nobuild" in COMMAND_LINE_TARGETS:
      target_firm = join("$BUILD_DIR", "${PROGNAME}.bin")
  else:
      target_elf = env.BuildProgram()
      target_firm = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)
  
  AlwaysBuild(env.Alias("nobuild", target_firm))
  target_buildprog = env.Alias("buildprog", target_firm, target_firm)
  
  #
  # Target: Print binary size
  #
  
  target_size = env.Alias(
      "size", target_elf,
      env.VerboseAction("$SIZEPRINTCMD", "Calculating size $SOURCE"))
  AlwaysBuild(target_size)
  
  #
  # Target: Upload by default .bin file
  #
  
  upload_protocol = env.subst("$UPLOAD_PROTOCOL")
  debug_server = env.BoardConfig().get("debug.tools", {}).get(
      upload_protocol, {}).get("server")
  upload_actions = []
  
  if upload_protocol == "mbed":
      upload_actions = [
          env.VerboseAction(env.AutodetectUploadPort, "Looking for upload disk..."),
          env.VerboseAction(env.UploadToDisk, "Uploading $SOURCE")
      ]
  
  elif upload_protocol.startswith("jlink"):
  
      def _jlink_cmd_script(env, source):
          build_dir = env.subst("$BUILD_DIR")
          if not isdir(build_dir):
              makedirs(build_dir)
          script_path = join(build_dir, "upload.jlink")
          commands = ["h", "loadbin %s,0x0" % source, "r", "q"]
          with open(script_path, "w") as fp:
              fp.write("\n".join(commands))
          return script_path
  
      env.Replace(
          __jlink_cmd_script=_jlink_cmd_script,
          UPLOADER="JLink.exe" if system() == "Windows" else "JLinkExe",
          UPLOADERFLAGS=[
              "-device", env.BoardConfig().get("debug", {}).get("jlink_device"),
              "-speed", "4000",
              "-if", ("jtag" if upload_protocol == "jlink-jtag" else "swd"),
              "-autoconnect", "1"
          ],
          UPLOADCMD="$UPLOADER $UPLOADERFLAGS -CommanderScript ${__jlink_cmd_script(__env__, SOURCE)}"
      )
      upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
  
  elif upload_protocol.startswith("blackmagic"):
      env.Replace(
          UPLOADER="$GDB",
          UPLOADERFLAGS=[
              "-nx",
              "--batch",
              "-ex", "target extended-remote $UPLOAD_PORT",
              "-ex", "monitor %s_scan" %
              ("jtag" if upload_protocol == "blackmagic-jtag" else "swdp"),
              "-ex", "attach 1",
              "-ex", "load",
              "-ex", "compare-sections",
              "-ex", "kill"
          ],
          UPLOADCMD="$UPLOADER $UPLOADERFLAGS $BUILD_DIR/${PROGNAME}.elf"
      )
      upload_actions = [
          env.VerboseAction(env.AutodetectUploadPort, "Looking for BlackMagic port..."),
          env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")
      ]
  
  elif debug_server and debug_server.get("package") == "tool-pyocd":
      env.Replace(
          UPLOADER=join(platform.get_package_dir("tool-pyocd") or "",
                        "pyocd-flashtool.py"),
          UPLOADERFLAGS=debug_server.get("arguments", [])[1:],
          UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS $SOURCE'
      )
      upload_actions = [
          env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")
      ]
  
  # custom upload tool
  elif "UPLOADCMD" in env:
      upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
  
  else:
      sys.stderr.write("Warning! Unknown upload protocol %s\n" % upload_protocol)
  
  AlwaysBuild(env.Alias("upload", target_firm, upload_actions))
  
  #
  # Default targets
  #
  
  Default([target_buildprog, target_size])
  