diff --git a/arduino/builder/sketch.go b/arduino/builder/sketch.go
index 454d06d02a1..b5a6a97abb9 100644
--- a/arduino/builder/sketch.go
+++ b/arduino/builder/sketch.go
@@ -24,6 +24,7 @@ import (
 	"github.com/arduino/arduino-cli/arduino/sketch"
 	"github.com/arduino/arduino-cli/i18n"
 	"github.com/arduino/go-paths-helper"
+	"github.com/arduino/go-properties-orderedmap"
 
 	"github.com/pkg/errors"
 )
@@ -169,3 +170,29 @@ func writeIfDifferent(source []byte, destPath *paths.Path) error {
 	// Source and destination are the same, don't write anything
 	return nil
 }
+
+// SetupBuildProperties adds the build properties related to the sketch to the
+// default board build properties map.
+func SetupBuildProperties(boardBuildProperties *properties.Map, buildPath *paths.Path, sketch *sketch.Sketch, optimizeForDebug bool) *properties.Map {
+	buildProperties := properties.NewMap()
+	buildProperties.Merge(boardBuildProperties)
+
+	if buildPath != nil {
+		buildProperties.SetPath("build.path", buildPath)
+	}
+	if sketch != nil {
+		buildProperties.Set("build.project_name", sketch.MainFile.Base())
+		buildProperties.SetPath("build.source.path", sketch.FullPath)
+	}
+	if optimizeForDebug {
+		if debugFlags, ok := buildProperties.GetOk("compiler.optimization_flags.debug"); ok {
+			buildProperties.Set("compiler.optimization_flags", debugFlags)
+		}
+	} else {
+		if releaseFlags, ok := buildProperties.GetOk("compiler.optimization_flags.release"); ok {
+			buildProperties.Set("compiler.optimization_flags", releaseFlags)
+		}
+	}
+
+	return buildProperties
+}
diff --git a/arduino/cores/packagemanager/package_manager.go b/arduino/cores/packagemanager/package_manager.go
index 135b0c856f3..175a783025b 100644
--- a/arduino/cores/packagemanager/package_manager.go
+++ b/arduino/cores/packagemanager/package_manager.go
@@ -359,6 +359,13 @@ func (pme *Explorer) ResolveFQBN(fqbn *cores.FQBN) (
 	}
 	buildProperties.Set("runtime.os", properties.GetOSSuffix())
 	buildProperties.Set("build.library_discovery_phase", "0")
+
+	if buildProperties.Get("build.board") == "" {
+		architecture := board.PlatformRelease.Platform.Architecture
+		defaultBuildBoard := strings.ToUpper(architecture + "_" + board.BoardID)
+		buildProperties.Set("build.board", defaultBuildBoard)
+	}
+
 	// Deprecated properties
 	buildProperties.Set("tools.avrdude.path", "{runtime.tools.avrdude.path}")
 	buildProperties.Set("ide_version", "10607")
diff --git a/arduino/cores/packagemanager/package_manager_test.go b/arduino/cores/packagemanager/package_manager_test.go
index cf14d9939f6..5cab6878485 100644
--- a/arduino/cores/packagemanager/package_manager_test.go
+++ b/arduino/cores/packagemanager/package_manager_test.go
@@ -67,7 +67,7 @@ func TestResolveFQBN(t *testing.T) {
 	pme, release := pm.NewExplorer()
 	defer release()
 
-	{
+	t.Run("NormalizeFQBN", func(t *testing.T) {
 		testNormalization := func(in, expected string) {
 			fqbn, err := cores.ParseFQBN(in)
 			require.Nil(t, err)
@@ -89,9 +89,9 @@ func TestResolveFQBN(t *testing.T) {
 		testNormalization("esp8266:esp8266:generic:baud=115200,wipe=sdk", "esp8266:esp8266:generic:wipe=sdk")
 		testNormalization("arduino:avr:mega:cpu=nonexistent", "ERROR")
 		testNormalization("arduino:avr:mega:nonexistent=blah", "ERROR")
-	}
+	})
 
-	{
+	t.Run("BoardAndBuildPropertiesArduinoUno", func(t *testing.T) {
 		fqbn, err := cores.ParseFQBN("arduino:avr:uno")
 		require.Nil(t, err)
 		require.NotNil(t, fqbn)
@@ -105,9 +105,14 @@ func TestResolveFQBN(t *testing.T) {
 		require.Equal(t, board.Name(), "Arduino Uno")
 		require.NotNil(t, props)
 		require.Equal(t, platformRelease, buildPlatformRelease)
-	}
 
-	{
+		require.Equal(t, "arduino", pkg.Name)
+		require.Equal(t, "avr", platformRelease.Platform.Architecture)
+		require.Equal(t, "uno", board.BoardID)
+		require.Equal(t, "atmega328p", props.Get("build.mcu"))
+	})
+
+	t.Run("BoardAndBuildPropertiesArduinoMega", func(t *testing.T) {
 		fqbn, err := cores.ParseFQBN("arduino:avr:mega")
 		require.Nil(t, err)
 		require.NotNil(t, fqbn)
@@ -121,9 +126,46 @@ func TestResolveFQBN(t *testing.T) {
 		require.Equal(t, board.Name(), "Arduino Mega or Mega 2560")
 		require.NotNil(t, props)
 		require.Equal(t, platformRelease, buildPlatformRelease)
-	}
+	})
 
-	{
+	t.Run("BoardAndBuildPropertiesArduinoMegaWithNonDefaultCpuOption", func(t *testing.T) {
+		fqbn, err := cores.ParseFQBN("arduino:avr:mega:cpu=atmega1280")
+		require.Nil(t, err)
+		require.NotNil(t, fqbn)
+		pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn)
+		require.Nil(t, err)
+		require.Equal(t, pkg, platformRelease.Platform.Package)
+		require.NotNil(t, platformRelease)
+		require.NotNil(t, platformRelease.Platform)
+		require.Equal(t, platformRelease, buildPlatformRelease)
+
+		require.Equal(t, "arduino", pkg.Name)
+		require.Equal(t, "avr", platformRelease.Platform.Architecture)
+		require.Equal(t, "mega", board.BoardID)
+		require.Equal(t, "atmega1280", props.Get("build.mcu"))
+		require.Equal(t, "AVR_MEGA", props.Get("build.board"))
+	})
+
+	t.Run("BoardAndBuildPropertiesArduinoMegaWithDefaultCpuOption", func(t *testing.T) {
+		fqbn, err := cores.ParseFQBN("arduino:avr:mega:cpu=atmega2560")
+		require.Nil(t, err)
+		require.NotNil(t, fqbn)
+		pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn)
+		require.Nil(t, err)
+		require.Equal(t, pkg, platformRelease.Platform.Package)
+		require.NotNil(t, platformRelease)
+		require.NotNil(t, platformRelease.Platform)
+		require.Equal(t, platformRelease, buildPlatformRelease)
+
+		require.Equal(t, "arduino", pkg.Name)
+		require.Equal(t, "avr", platformRelease.Platform.Architecture)
+		require.Equal(t, "mega", board.BoardID)
+		require.Equal(t, "atmega2560", props.Get("build.mcu"))
+		require.Equal(t, "AVR_MEGA2560", props.Get("build.board"))
+
+	})
+
+	t.Run("BoardAndBuildPropertiesForReferencedArduinoUno", func(t *testing.T) {
 		// Test a board referenced from the main AVR arduino platform
 		fqbn, err := cores.ParseFQBN("referenced:avr:uno")
 		require.Nil(t, err)
@@ -140,9 +182,56 @@ func TestResolveFQBN(t *testing.T) {
 		require.NotNil(t, buildPlatformRelease)
 		require.NotNil(t, buildPlatformRelease.Platform)
 		require.Equal(t, buildPlatformRelease.Platform.String(), "arduino:avr")
-	}
+	})
 
-	{
+	t.Run("BoardAndBuildPropertiesForArduinoDue", func(t *testing.T) {
+		fqbn, err := cores.ParseFQBN("arduino:sam:arduino_due_x")
+		require.Nil(t, err)
+		require.NotNil(t, fqbn)
+		pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn)
+		require.Nil(t, err)
+		require.Equal(t, pkg, platformRelease.Platform.Package)
+		require.Equal(t, platformRelease, buildPlatformRelease)
+
+		require.Equal(t, "arduino", pkg.Name)
+		require.Equal(t, "sam", platformRelease.Platform.Architecture)
+		require.Equal(t, "arduino_due_x", board.BoardID)
+		require.Equal(t, "cortex-m3", props.Get("build.mcu"))
+	})
+
+	t.Run("BoardAndBuildPropertiesForCustomArduinoYun", func(t *testing.T) {
+		fqbn, err := cores.ParseFQBN("my_avr_platform:avr:custom_yun")
+		require.Nil(t, err)
+		require.NotNil(t, fqbn)
+		pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn)
+		require.Nil(t, err)
+		require.Equal(t, pkg, platformRelease.Platform.Package)
+		require.NotEqual(t, platformRelease, buildPlatformRelease)
+
+		require.Equal(t, "my_avr_platform", pkg.Name)
+		require.Equal(t, "avr", platformRelease.Platform.Architecture)
+		require.Equal(t, "custom_yun", board.BoardID)
+		require.Equal(t, "atmega32u4", props.Get("build.mcu"))
+		require.Equal(t, "AVR_YUN", props.Get("build.board"))
+	})
+
+	t.Run("BoardAndBuildPropertiesForWatterotCore", func(t *testing.T) {
+		fqbn, err := cores.ParseFQBN("watterott:avr:attiny841:core=spencekonde,info=info")
+		require.Nil(t, err)
+		require.NotNil(t, fqbn)
+		pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn)
+		require.Nil(t, err)
+		require.Equal(t, pkg, platformRelease.Platform.Package)
+		require.Equal(t, platformRelease, buildPlatformRelease)
+
+		require.Equal(t, "watterott", pkg.Name)
+		require.Equal(t, "avr", platformRelease.Platform.Architecture)
+		require.Equal(t, "attiny841", board.BoardID)
+		require.Equal(t, "tiny841", props.Get("build.core"))
+		require.Equal(t, "tiny14", props.Get("build.variant"))
+	})
+
+	t.Run("BoardAndBuildPropertiesForReferencedFeatherM0", func(t *testing.T) {
 		// Test a board referenced from the Adafruit SAMD core (this tests
 		// deriving where the package and core name are different)
 		fqbn, err := cores.ParseFQBN("referenced:samd:feather_m0")
@@ -160,9 +249,9 @@ func TestResolveFQBN(t *testing.T) {
 		require.NotNil(t, buildPlatformRelease)
 		require.NotNil(t, buildPlatformRelease.Platform)
 		require.Equal(t, buildPlatformRelease.Platform.String(), "adafruit:samd")
-	}
+	})
 
-	{
+	t.Run("BoardAndBuildPropertiesForNonExistentPackage", func(t *testing.T) {
 		// Test a board referenced from a non-existent package
 		fqbn, err := cores.ParseFQBN("referenced:avr:dummy_invalid_package")
 		require.Nil(t, err)
@@ -177,9 +266,9 @@ func TestResolveFQBN(t *testing.T) {
 		require.Equal(t, board.Name(), "Referenced dummy with invalid package")
 		require.Nil(t, props)
 		require.Nil(t, buildPlatformRelease)
-	}
+	})
 
-	{
+	t.Run("BoardAndBuildPropertiesForNonExistentArchitecture", func(t *testing.T) {
 		// Test a board referenced from a non-existent platform/architecture
 		fqbn, err := cores.ParseFQBN("referenced:avr:dummy_invalid_platform")
 		require.Nil(t, err)
@@ -194,9 +283,9 @@ func TestResolveFQBN(t *testing.T) {
 		require.Equal(t, board.Name(), "Referenced dummy with invalid platform")
 		require.Nil(t, props)
 		require.Nil(t, buildPlatformRelease)
-	}
+	})
 
-	{
+	t.Run("BoardAndBuildPropertiesForNonExistentCore", func(t *testing.T) {
 		// Test a board referenced from a non-existent core
 		// Note that ResolveFQBN does not actually check this currently
 		fqbn, err := cores.ParseFQBN("referenced:avr:dummy_invalid_core")
@@ -214,7 +303,41 @@ func TestResolveFQBN(t *testing.T) {
 		require.NotNil(t, buildPlatformRelease)
 		require.NotNil(t, buildPlatformRelease.Platform)
 		require.Equal(t, buildPlatformRelease.Platform.String(), "arduino:avr")
-	}
+	})
+
+	t.Run("AddBuildBoardPropertyIfMissing", func(t *testing.T) {
+		fqbn, err := cores.ParseFQBN("my_avr_platform:avr:mymega")
+		require.Nil(t, err)
+		require.NotNil(t, fqbn)
+		pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn)
+		require.Nil(t, err)
+		require.Equal(t, pkg, platformRelease.Platform.Package)
+		require.Equal(t, platformRelease, buildPlatformRelease)
+
+		require.Equal(t, "my_avr_platform", pkg.Name)
+		require.NotNil(t, platformRelease)
+		require.NotNil(t, platformRelease.Platform)
+		require.Equal(t, "avr", platformRelease.Platform.Architecture)
+		require.Equal(t, "mymega", board.BoardID)
+		require.Equal(t, "atmega2560", props.Get("build.mcu"))
+		require.Equal(t, "AVR_MYMEGA", props.Get("build.board"))
+	})
+
+	t.Run("AddBuildBoardPropertyIfNotMissing", func(t *testing.T) {
+		fqbn, err := cores.ParseFQBN("my_avr_platform:avr:mymega:cpu=atmega1280")
+		require.Nil(t, err)
+		require.NotNil(t, fqbn)
+		pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn)
+		require.Nil(t, err)
+		require.Equal(t, pkg, platformRelease.Platform.Package)
+		require.Equal(t, platformRelease, buildPlatformRelease)
+
+		require.Equal(t, "my_avr_platform", pkg.Name)
+		require.Equal(t, "avr", platformRelease.Platform.Architecture)
+		require.Equal(t, "mymega", board.BoardID)
+		require.Equal(t, "atmega1280", props.Get("build.mcu"))
+		require.Equal(t, "MYMEGA1280", props.Get("build.board"))
+	})
 }
 
 func TestBoardOptionsFunctions(t *testing.T) {
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/arduino/sam/boards.txt b/arduino/cores/packagemanager/testdata/custom_hardware/arduino/sam/boards.txt
new file mode 100644
index 00000000000..5503786126c
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/arduino/sam/boards.txt
@@ -0,0 +1,49 @@
+
+arduino_due_x_dbg.name=Arduino Due (Programming Port)
+arduino_due_x_dbg.vid.0=0x2341
+arduino_due_x_dbg.pid.0=0x003d
+arduino_due_x_dbg.vid.1=0x2A03
+arduino_due_x_dbg.pid.1=0x003d
+arduino_due_x_dbg.upload.tool=bossac
+arduino_due_x_dbg.upload.protocol=sam-ba
+arduino_due_x_dbg.upload.maximum_size=524288
+arduino_due_x_dbg.upload.use_1200bps_touch=true
+arduino_due_x_dbg.upload.wait_for_upload_port=false
+arduino_due_x_dbg.upload.native_usb=false
+arduino_due_x_dbg.build.mcu=cortex-m3
+arduino_due_x_dbg.build.f_cpu=84000000L
+arduino_due_x_dbg.build.usb_manufacturer="Arduino LLC"
+arduino_due_x_dbg.build.usb_product="Arduino Due"
+arduino_due_x_dbg.build.board=SAM_DUE
+arduino_due_x_dbg.build.core=arduino
+arduino_due_x_dbg.build.extra_flags=-D__SAM3X8E__ -mthumb {build.usb_flags}
+arduino_due_x_dbg.build.ldscript=linker_scripts/gcc/flash.ld
+arduino_due_x_dbg.build.variant=arduino_due_x
+arduino_due_x_dbg.build.variant_system_lib=libsam_sam3x8e_gcc_rel.a
+arduino_due_x_dbg.build.vid=0x2341
+arduino_due_x_dbg.build.pid=0x003e
+
+arduino_due_x.name=Arduino Due (Native USB Port)
+arduino_due_x.vid.0=0x2341
+arduino_due_x.pid.0=0x003e
+arduino_due_x.vid.1=0x2A03
+arduino_due_x.pid.1=0x003e
+arduino_due_x.upload.tool=bossac
+arduino_due_x.upload.protocol=sam-ba
+arduino_due_x.upload.maximum_size=524288
+arduino_due_x.upload.use_1200bps_touch=true
+arduino_due_x.upload.wait_for_upload_port=true
+arduino_due_x.upload.native_usb=true
+arduino_due_x.build.mcu=cortex-m3
+arduino_due_x.build.f_cpu=84000000L
+arduino_due_x.build.usb_manufacturer="Arduino LLC"
+arduino_due_x.build.usb_product="Arduino Due"
+arduino_due_x.build.board=SAM_DUE
+arduino_due_x.build.core=arduino
+arduino_due_x.build.extra_flags=-D__SAM3X8E__ -mthumb {build.usb_flags}
+arduino_due_x.build.ldscript=linker_scripts/gcc/flash.ld
+arduino_due_x.build.variant=arduino_due_x
+arduino_due_x.build.variant_system_lib=libsam_sam3x8e_gcc_rel.a
+arduino_due_x.build.vid=0x2341
+arduino_due_x.build.pid=0x003e
+
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/arduino/sam/platform.txt b/arduino/cores/packagemanager/testdata/custom_hardware/arduino/sam/platform.txt
new file mode 100644
index 00000000000..5a330240f3e
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/arduino/sam/platform.txt
@@ -0,0 +1,108 @@
+
+# Arduino SAM Core and platform.
+# ------------------------------
+#
+# For more info:
+# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification
+
+name=Arduino ARM (32-bits) Boards
+version=1.6.12
+
+# SAM3 compile variables
+# ----------------------
+
+compiler.warning_flags=-w
+compiler.warning_flags.none=-w
+compiler.warning_flags.default=
+compiler.warning_flags.more=-Wall
+compiler.warning_flags.all=-Wall -Wextra
+
+compiler.path={runtime.tools.arm-none-eabi-gcc-4.8.3-2014q1.path}/bin/
+compiler.c.cmd=arm-none-eabi-gcc
+compiler.c.flags=-c -g -Os {compiler.warning_flags} -std=gnu11 -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -Dprintf=iprintf -MMD
+compiler.c.elf.cmd=arm-none-eabi-gcc
+compiler.c.elf.flags=-Os -Wl,--gc-sections
+compiler.S.cmd=arm-none-eabi-gcc
+compiler.S.flags=-c -g -x assembler-with-cpp -MMD
+compiler.cpp.cmd=arm-none-eabi-g++
+compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++11 -ffunction-sections -fdata-sections -nostdlib -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -MMD
+compiler.ar.cmd=arm-none-eabi-ar
+compiler.ar.flags=rcs
+compiler.objcopy.cmd=arm-none-eabi-objcopy
+compiler.objcopy.eep.flags=-O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0
+compiler.elf2hex.flags=-O binary
+compiler.elf2hex.cmd=arm-none-eabi-objcopy
+compiler.ldflags=
+compiler.size.cmd=arm-none-eabi-size
+compiler.define=-DARDUINO=
+compiler.combine.flags=-u _sbrk -u link -u _close -u _fstat -u _isatty -u _lseek -u _read -u _write -u _exit -u kill -u _getpid
+
+# This can be overridden in boards.txt
+build.extra_flags=
+
+# These can be overridden in platform.local.txt
+compiler.c.extra_flags=
+compiler.c.elf.extra_flags=
+compiler.cpp.extra_flags=
+compiler.S.extra_flags=
+compiler.ar.extra_flags=
+compiler.elf2hex.extra_flags=
+
+
+compiler.libsam.c.flags="-I{build.system.path}/libsam" "-I{build.system.path}/CMSIS/CMSIS/Include/" "-I{build.system.path}/CMSIS/Device/ATMEL/"
+
+# USB Flags
+# ---------
+build.usb_flags=-DUSB_VID={build.vid} -DUSB_PID={build.pid} -DUSBCON '-DUSB_MANUFACTURER={build.usb_manufacturer}' '-DUSB_PRODUCT={build.usb_product}'
+
+# Default usb manufacturer will be replaced at compile time using
+# numeric vendor ID if available or by board's specific value.
+build.usb_manufacturer="Unknown"
+
+
+# SAM3 compile patterns
+# ---------------------
+
+## Compile c files
+recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -mcpu={build.mcu} -mthumb -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {compiler.libsam.c.flags} {includes} "{source_file}" -o "{object_file}"
+
+## Compile c++ files
+recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -mcpu={build.mcu} -mthumb -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {compiler.libsam.c.flags} {includes} "{source_file}" -o "{object_file}"
+
+## Compile S files
+recipe.S.o.pattern="{compiler.path}{compiler.S.cmd}" {compiler.S.flags} -mcpu={build.mcu} -mthumb -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.S.extra_flags} {build.extra_flags} {compiler.libsam.c.flags} {includes} "{source_file}" -o "{object_file}"
+
+## Create archives
+# archive_file_path is needed for backwards compatibility with IDE 1.6.5 or older, IDE 1.6.6 or newer overrides this value
+archive_file_path={build.path}/{archive_file}
+recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}"
+
+## Combine gc-sections, archives, and objects
+recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -mcpu={build.mcu} -mthumb {compiler.c.elf.flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--start-group {compiler.combine.flags} {object_files} "{build.variant.path}/{build.variant_system_lib}" "{build.path}/{archive_file}" -Wl,--end-group -lm -lgcc
+
+## Create output (.bin file)
+recipe.objcopy.bin.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin"
+
+## Save hex
+recipe.output.tmp_file={build.project_name}.bin
+recipe.output.save_file={build.project_name}.{build.variant}.bin
+
+## Compute size
+recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf"
+recipe.size.regex=\.text\s+([0-9]+).*
+
+
+# SAM3 Uploader tools
+# -------------------
+
+# BOSSA
+tools.bossac.path={runtime.tools.bossac.path}
+tools.bossac.cmd=bossac
+tools.bossac.cmd.windows=bossac.exe
+
+tools.bossac.upload.params.verbose=-i -d
+tools.bossac.upload.params.quiet=
+tools.bossac.upload.params.verify=-v
+tools.bossac.upload.pattern="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U {upload.native_usb} -e -w {upload.verify} -b "{build.path}/{build.project_name}.bin" -R
+
+tools.bossac_remote.upload.pattern=/usr/bin/run-bossac {upload.verbose} --port=ttyATH0 -U {upload.native_usb} -e -w -v -b /tmp/sketch.bin -R
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/boards.txt b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/boards.txt
new file mode 100644
index 00000000000..bf794a46f71
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/boards.txt
@@ -0,0 +1,89 @@
+menu.cpu=Processor
+
+custom_yun.name=Arduino Yún
+custom_yun.upload.via_ssh=true
+
+custom_yun.build.core=arduino:arduino
+custom_yun.bootloader.tool=arduino:avrdude
+custom_yun.upload.tool=arduino:avrdude
+
+custom_yun.vid.0=0x2341
+custom_yun.pid.0=0x0041
+custom_yun.vid.1=0x2341
+custom_yun.pid.1=0x8041
+custom_yun.upload.protocol=avr109
+custom_yun.upload.maximum_size=28672
+custom_yun.upload.maximum_data_size=2560
+custom_yun.upload.speed=57600
+custom_yun.upload.disable_flushing=true
+custom_yun.upload.use_1200bps_touch=true
+custom_yun.upload.wait_for_upload_port=true
+
+custom_yun.bootloader.low_fuses=0xff
+custom_yun.bootloader.high_fuses=0xd8
+custom_yun.bootloader.extended_fuses=0xfb
+custom_yun.bootloader.file=caterina/Caterina-custom_yun.hex
+custom_yun.bootloader.unlock_bits=0x3F
+custom_yun.bootloader.lock_bits=0x2F
+
+custom_yun.build.mcu=atmega32u4
+custom_yun.build.f_cpu=16000000L
+custom_yun.build.vid=0x2341
+custom_yun.build.pid=0x8041
+custom_yun.build.usb_product="Arduino My"
+custom_yun.build.board=AVR_YUN
+custom_yun.build.variant=arduino:yun
+custom_yun.build.extra_flags={build.usb_flags}
+
+mymega.name=Arduino Mega or Mega 2560
+
+mymega.vid.0=0x2341
+mymega.pid.0=0x0010
+mymega.vid.1=0x2341
+mymega.pid.1=0x0042
+mymega.vid.2=0x2A03
+mymega.pid.2=0x0010
+mymega.vid.3=0x2A03
+mymega.pid.3=0x0042
+
+mymega.upload.tool=avrdude
+mymega.upload.maximum_data_size=8192
+
+mymega.bootloader.tool=avrdude
+mymega.bootloader.low_fuses=0xFF
+mymega.bootloader.unlock_bits=0x3F
+mymega.bootloader.lock_bits=0x0F
+
+mymega.build.f_cpu=16000000L
+mymega.build.core=arduino
+mymega.build.variant=mega
+
+mymega.menu.cpu.atmega2560=ATmega2560 (Mega 2560)
+
+mymega.menu.cpu.atmega2560.upload.protocol=wiring
+mymega.menu.cpu.atmega2560.upload.maximum_size=253952
+mymega.menu.cpu.atmega2560.upload.speed=115200
+
+mymega.menu.cpu.atmega2560.bootloader._folder=stk500v2
+mymega.menu.cpu.atmega2560.bootloader.high_fuses=0xD8
+mymega.menu.cpu.atmega2560.bootloader.extended_fuses=0xFD
+mymega.menu.cpu.atmega2560.bootloader.file={bootloader._folder}/stk500boot_v2_mega2560.hex
+
+mymega.menu.cpu.atmega2560.build.mcu=atmega2560
+# Do not define build.board to test autogeneration
+#mymega.menu.cpu.atmega2560.build.board=AVR_MYMEGA2560
+
+mymega.menu.cpu.atmega1280=ATmega1280
+
+mymega.menu.cpu.atmega1280.upload.protocol=arduino
+mymega.menu.cpu.atmega1280.upload.maximum_size=126976
+mymega.menu.cpu.atmega1280.upload.speed=57600
+
+mymega.menu.cpu.atmega1280.bootloader._folder=atmega
+mymega.menu.cpu.atmega1280.bootloader.high_fuses=0xDA
+mymega.menu.cpu.atmega1280.bootloader.extended_fuses=0xF5
+mymega.menu.cpu.atmega1280.bootloader.file={bootloader._folder}/ATmegaBOOT_168_atmega1280.hex
+
+mymega.menu.cpu.atmega1280.build.mcu=atmega1280
+# define custom build.board for testing
+mymega.menu.cpu.atmega1280.build.board=MYMEGA1280
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/bootloaders/stk500v2/stk500boot_v2_mega2560.hex b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/bootloaders/stk500v2/stk500boot_v2_mega2560.hex
new file mode 100644
index 00000000000..c52e690a102
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/bootloaders/stk500v2/stk500boot_v2_mega2560.hex
@@ -0,0 +1,469 @@
+:020000023000CC
+:10E000000D9489F10D94B2F10D94B2F10D94B2F129
+:10E010000D94B2F10D94B2F10D94B2F10D94B2F1F0
+:10E020000D94B2F10D94B2F10D94B2F10D94B2F1E0
+:10E030000D94B2F10D94B2F10D94B2F10D94B2F1D0
+:10E040000D94B2F10D94B2F10D94B2F10D94B2F1C0
+:10E050000D94B2F10D94B2F10D94B2F10D94B2F1B0
+:10E060000D94B2F10D94B2F10D94B2F10D94B2F1A0
+:10E070000D94B2F10D94B2F10D94B2F10D94B2F190
+:10E080000D94B2F10D94B2F10D94B2F10D94B2F180
+:10E090000D94B2F10D94B2F10D94B2F10D94B2F170
+:10E0A0000D94B2F10D94B2F10D94B2F10D94B2F160
+:10E0B0000D94B2F10D94B2F10D94B2F10D94B2F150
+:10E0C0000D94B2F10D94B2F10D94B2F10D94B2F140
+:10E0D0000D94B2F10D94B2F10D94B2F10D94B2F130
+:10E0E0000D94B2F141546D656761323536300041AF
+:10E0F000726475696E6F206578706C6F72657220DE
+:10E1000073746B3530305632206279204D4C530099
+:10E11000426F6F746C6F616465723E004875683F52
+:10E1200000436F6D70696C6564206F6E203D200048
+:10E130004350552054797065202020203D20005FF9
+:10E140005F4156525F415243485F5F3D2000415658
+:10E1500052204C696243205665723D20004743437C
+:10E160002056657273696F6E203D20004350552024
+:10E1700049442020202020203D20004C6F7720663D
+:10E18000757365202020203D20004869676820665F
+:10E190007573652020203D200045787420667573D6
+:10E1A00065202020203D20004C6F636B2066757336
+:10E1B000652020203D20004D617220203720323024
+:10E1C000313300312E362E3800342E332E350056A2
+:10E1D00023202020414444522020206F7020636F70
+:10E1E00064652020202020696E73747275637469E1
+:10E1F0006F6E2061646472202020496E74657272B3
+:10E20000757074006E6F20766563746F7200726A49
+:10E210006D702020006A6D70200057686174207056
+:10E220006F72743A00506F7274206E6F7420737541
+:10E2300070706F72746564004D7573742062652030
+:10E2400061206C6574746572002000577269747483
+:10E25000696E672045450052656164696E672045B7
+:10E26000450045452065727220636E743D00504F35
+:10E27000525400303D5A65726F2061646472003FF1
+:10E280003D43505520737461747300403D454550C3
+:10E29000524F4D207465737400423D426C696E6B41
+:10E2A000204C454400453D44756D70204545505215
+:10E2B0004F4D00463D44756D7020464C415348001B
+:10E2C000483D48656C70004C3D4C69737420492F83
+:10E2D0004F20506F72747300513D51756974005234
+:10E2E0003D44756D702052414D00563D73686F7707
+:10E2F00020696E7465727275707420566563746FF0
+:10E30000727300593D506F727420626C696E6B00BD
+:10E310002A0011241FBECFEFD1E2DEBFCDBF01E046
+:10E320000CBF12E0A0E0B2E0EEE1FDEF03E00BBFB6
+:10E3300002C007900D92A030B107D9F712E0A0E01B
+:10E34000B2E001C01D92AE30B107E1F70F9460F367
+:10E350000D948DFE01E20EBF0FEF0DBF11241FBE05
+:10E360000D9460F30D9400F020E030E040ED57E0B4
+:10E3700005C0FA013197F1F72F5F3F4F2817390792
+:10E38000C0F308959C01260F311DC901A0E0B0E043
+:10E390002F5F3F4FABBFFC018791882361F08093D3
+:10E3A000C6008091C00086FFFCCF8091C0008064D1
+:10E3B0008093C000EACF08958DE08093C6008091DD
+:10E3C000C00086FFFCCF8091C00080648093C000B5
+:10E3D0008AE08093C6008091C00086FFFCCF8091C8
+:10E3E000C00080648093C00008950F94C2F10F9420
+:10E3F000DCF10895FC019081992359F09093C600B7
+:10E400008091C00086FFFCCF8091C0008064809323
+:10E41000C0003196992379F70895282F982F929567
+:10E420009F70892F805D8A3308F0895F8093C600D2
+:10E430008091C00086FFFCCF8091C00080648093F3
+:10E44000C000822F8F70982F905D9A3308F0995FEB
+:10E450009093C6008091C00086FFFCCF8091C000E1
+:10E4600080648093C00008959C01FB01853691056E
+:10E470001CF46330710594F0C90164E670E00F94F8
+:10E480002EFE605D7F4F6093C6008091C00086FFC6
+:10E49000FCCF8091C00080648093C0002B30310598
+:10E4A00014F43297B4F0C90164E670E00F942EFEC4
+:10E4B0006AE070E00F942EFE605D7F4F6093C600AF
+:10E4C0008091C00086FFFCCF8091C0008064809363
+:10E4D000C000C9016AE070E00F942EFEC0968093E0
+:10E4E000C6008091C00086FFFCCF8091C000806490
+:10E4F0008093C00008951F93182F8EE692EE60E07F
+:10E500000F94C2F11093C6008091C00086FFFCCF2B
+:10E510008091C00080648093C0000F94DCF11F9153
+:10E5200008952F923F924F925F926F927F928F92B7
+:10E530009F92AF92BF92CF92DF92EF92FF920F9392
+:10E540001F93DF93CF93CDB7DEB762970FB6F894E2
+:10E55000DEBF0FBECDBF382E622ECA01DB015C01CB
+:10E560006D01772420E2222E2E010894411C511CBB
+:10E570008BC081E0A81680E0B80681E0C80680E084
+:10E58000D80628F0C601AA27BB270F940DF2BB2797
+:10E59000AD2D9C2D8B2D0F940DF28A2D0F940DF225
+:10E5A0002092C6008091C00086FFFCCF8091C00001
+:10E5B00080648093C0009DE29093C6008091C0006B
+:10E5C00086FFFCCF8091C00080648093C0002092C1
+:10E5D000C6008091C00086FFFCCF8091C00080649F
+:10E5E0008093C00019828601750188249924A1E0D6
+:10E5F0003A1651F03A1620F0B2E03B1661F409C029
+:10E600000BBFF701779007C0C7010F9477FE782EF4
+:10E6100002C0F7017080872D0F940DF22092C60082
+:10E620008091C00086FFFCCF8091C0008064809301
+:10E63000C000872D8052F401EF70F0708F3520F408
+:10E64000E40DF51D708204C0E40DF51D8EE280839B
+:10E650000894E11CF11C011D111D0894811C911CE2
+:10E6600090E18916910409F0C2CF80E190E0A0E02A
+:10E67000B0E0A80EB91ECA1EDB1E198AC2010F9493
+:10E68000FAF10F94DCF16A94662009F072CF629679
+:10E690000FB6F894DEBF0FBECDBFCF91DF911F91B3
+:10E6A0000F91FF90EF90DF90CF90BF90AF909F9031
+:10E6B0008F907F906F905F904F903F902F90089534
+:10E6C0002F923F924F925F926F927F928F929F9282
+:10E6D000AF92BF92CF92DF92EF92FF920F931F9370
+:10E6E000DF93CF93CDB7DEB7CD53D1400FB6F894BB
+:10E6F000DEBF0FBECDBF01E20EBF0FEF0DBF94B75F
+:10E70000F894A89514BE80916000886180936000A1
+:10E7100010926000789493FF05C0E0910002F091A0
+:10E7200001021995279A2F9A8091C00082608093E8
+:10E73000C00080E18093C40088E18093C1000000A4
+:10E74000EE24FF24870144E0A42EB12CCC24DD2448
+:10E7500024C0C5010197F1F70894E11CF11C011DCB
+:10E76000111D21E2E2162EE4F20620E0020720E06D
+:10E77000120718F031E0C32ED12CC801B70127ECE5
+:10E780003BE140E050E00F9441FE611571058105C9
+:10E79000910519F485B1805885B98091C00087FD35
+:10E7A00003C0C114D104A9F2A6014F5F5F4FC25E3E
+:10E7B000DE4F59834883CE51D140C25EDE4F8881FF
+:10E7C0009981CE51D140019711F00D9410FEC05D9A
+:10E7D000DE4F19821882C053D14060E0C15DDE4F28
+:10E7E0001882CF52D14088249924C35DDE4F19820C
+:10E7F0001882CD52D140C05EDE4F188219821A8233
+:10E800001B82C052D140CE5CDE4F188219821A8220
+:10E810001B82C253D140EE24FF2487010BBFF701B6
+:10E8200007911691C45CDE4F19830883CC53D14005
+:10E830000D940BFEC25EDE4F28813981CE51D1404E
+:10E840002130310509F52091C600C25EDE4F1982E4
+:10E850001882CE51D14022C02F5F3F4F4F4F5F4FA4
+:10E86000213082E138078AE7480780E0580780F0C6
+:10E87000C45CDE4FE881F981CC53D140EF5FFF4F9C
+:10E8800019F0EE27FF27099420E030E040E050E047
+:10E890008091C00087FFE0CF2091C600C35DDE4FAE
+:10E8A00048815981CD52D1404F5F5F4FC35DDE4FEC
+:10E8B00059834883CD52D140213209F063C64A3092
+:10E8C000510508F05FC60894811C911C53E0851621
+:10E8D000910409F059C600E010E018C081E280936D
+:10E8E000C6008091C00086FFFCCF8091C00080648C
+:10E8F0008093C0002F5F3F4F2931310579F70F9486
+:10E90000DCF10F5F1F4F0530110519F020E030E0FA
+:10E91000E5CF10920A0210920B0210920C02109294
+:10E920000D02109206021092070210920802109235
+:10E930000902109202021092030210920402109235
+:10E9400005028FEE90EE60E00F94F5F180E191EE1C
+:10E9500060E00F94C2F18091C00087FFFCCF9091DE
+:10E96000C600903608F09F759032B8F09093C600BC
+:10E970008091C00086FFFCCF8091C00080648093AE
+:10E98000C000A0E2A093C6008091C00086FFFCCF2B
+:10E990008091C00080648093C000983409F4D7C18E
+:10E9A0009934B8F4923409F459C1933458F490333B
+:10E9B00019F1903308F4E3C59F33A1F1903409F0C5
+:10E9C000DEC5BDC0953409F470C1963409F0D7C5D1
+:10E9D00098C1923509F42BC2933538F49C3409F46C
+:10E9E000F5C1913509F0CBC518C2963509F445C279
+:10E9F000993509F0C4C567C483E792EE62E00F94CD
+:10EA0000F5F110920602109207021092080210927D
+:10EA1000090210920A0210920B0210920C0210923C
+:10EA20000D0213C18FE792EE62E00F94F5F18FEEC5
+:10EA300090EE60E00F94F5F181E291EE60E00F94CA
+:10EA4000C2F187EB91EE60E00F94F5F180E391EE77
+:10EA500060E00F94C2F184EE90EE60E00F94F5F167
+:10EA60008FE391EE60E00F94C2F186E090E061E008
+:10EA700070E00F9434F20F94DCF18DE591EE60E0DC
+:10EA80000F94C2F189EC91EE60E00F94F5F18EE401
+:10EA900091EE60E00F94C2F183EC91EE60E00F9490
+:10EAA000F5F18CE691EE60E00F94C2F18EE10F94E7
+:10EAB0000DF288E90F940DF281E00F940DF20F949E
+:10EAC000DCF18BE791EE60E00F94C2F119E0E0E039
+:10EAD000F0E010935700E4918E2F0F940DF20F94F5
+:10EAE000DCF18AE891EE60E00F94C2F1E3E0F0E03F
+:10EAF00010935700E4918E2F0F940DF20F94DCF1D8
+:10EB000089E991EE60E00F94C2F1E2E0F0E0109349
+:10EB10005700E4918E2F0F940DF20F94DCF188EAE8
+:10EB200091EE60E00F94C2F1E1E0F0E01093570045
+:10EB30001491812F0F940DF20F94DCF107CF8BE825
+:10EB400092EE62E00F94F5F18BE492EE60E00F94A8
+:10EB5000F5F10F94DCF100E010E019C0C8016F2D51
+:10EB60000F947FFEFF2031F489E492EE60E00F9471
+:10EB7000C2F10BC0F092C6008091C00086FFFCCFAE
+:10EB80008091C00080648093C0000F5F1F4FC80158
+:10EB900081519F41A0E0B0E0ABBFFC01F790BAE229
+:10EBA000FB1621F0E2E000301E07C1F60F94DCF105
+:10EBB0000F94DCF187E592EE60E00F94F5F10F948D
+:10EBC000DCF1CC24DD2400E010E01EC0C8010F946D
+:10EBD00077FEF82E882331F489E492EE60E00F94FA
+:10EBE000C2F10BC08093C6008091C00086FFFCCFAD
+:10EBF0008091C00080648093C000FE1419F00894D6
+:10EC0000C11CD11C0F5F1F4FC80181519F41A0E063
+:10EC1000B0E0ABBFFC01E790FAE2EF1621F022E092
+:10EC20000030120799F60F94DCF10F94DCF182E6C4
+:10EC300092EE60E00F94C2F1C60161E070E00F94C3
+:10EC400034F20F94DCF10F94DCF110920202109276
+:10EC50000302109204021092050278CE89E992EE26
+:10EC600062E00F94F5F1279A2F9A16C02F9880E052
+:10EC700090E0E0EDF7E03197F1F7019684369105E9
+:10EC8000C1F72F9A80E090E0E0EDF7E03197F1F7DF
+:10EC9000019684369105C1F78091C00087FFE6CFC9
+:10ECA0008091C00087FFFCCF64C485EA92EE62E0E9
+:10ECB0000F94F5F140910202509103026091040219
+:10ECC0007091050281E020E10F9491F2809102029F
+:10ECD00090910302A0910402B091050280509F4FD1
+:10ECE000AF4FBF4F8093020290930302A0930402A0
+:10ECF000B093050280509041A040B04008F426CE69
+:10ED0000A4CF83EB92EE62E00F94F5F140910602FE
+:10ED100050910702609108027091090280E020E1A1
+:10ED20000F9491F28091060290910702A09108023F
+:10ED3000B091090280509F4FAF4FBF4F80930602A2
+:10ED400090930702A0930802B0930902FFCD80ECD4
+:10ED500092EE62E00F94F5F183E792EE60E00F949B
+:10ED6000F5F18FE792EE60E00F94F5F18BE892EE0B
+:10ED700060E00F94F5F189E992EE60E00F94F5F10F
+:10ED800085EA92EE60E00F94F5F183EB92EE60E09D
+:10ED90000F94F5F180EC92EE60E00F94F5F187ECC2
+:10EDA00092EE60E00F94F5F188ED92EE60E00F9442
+:10EDB000F5F18FED92EE60E00F94F5F18AEE92EEB0
+:10EDC00060E00F94F5F183E093EEBDCD87EC92EE19
+:10EDD00062E00F94F5F181E40F947BF282E40F94EA
+:10EDE0007BF283E40F947BF284E40F947BF285E45E
+:10EDF0000F947BF286E40F947BF287E40F947BF20E
+:10EE000088E40F947BF28AE40F947BF28BE40F94F6
+:10EE10007BF28CE40F947BF299CD88ED92EE62E068
+:10EE20000F94F5F1772473948824992409C48FED05
+:10EE300092EE62E00F94F5F140910A0250910B02BC
+:10EE400060910C0270910D0282E020E10F9491F22A
+:10EE500080910A0290910B02A0910C02B0910D02D8
+:10EE600080509F4FAF4FBF4F80930A0290930B0289
+:10EE7000A0930C02B0930D0269CD8AEE92EE62E08F
+:10EE80000F94F5F184EE90EE60E00F94F5F18FECC5
+:10EE900091EE60E00F94F5F1662477244301CC5D98
+:10EEA000DE4F19821882C452D140D401C301B695F5
+:10EEB000A79597958795CA5DDE4F88839983AA8326
+:10EEC000BB83C652D140CC5DDE4FA881B981C4520C
+:10EED000D1401196CC5DDE4FB983A883C452D14096
+:10EEE000CD0162E070E00F9434F2B0E2B093C6005E
+:10EEF0008091C00086FFFCCF8091C0008064809329
+:10EF0000C000EDE2E093C6008091C00086FFFCCF18
+:10EF10008091C00080648093C000F0E2F093C6004E
+:10EF20008091C00086FFFCCF8091C00080648093F8
+:10EF3000C000CA5DDE4FE880F9800A811B81C6529D
+:10EF4000D140BB27A12F902F8F2D0F940DF2CA5DBA
+:10EF5000DE4F8881C652D1400F940DF2B0E2FB2EF5
+:10EF6000F092C6008091C00086FFFCCF8091C00067
+:10EF700080648093C0000DE30093C6008091C000C0
+:10EF800086FFFCCF8091C00080648093C00010E2B7
+:10EF90001093C6008091C00086FFFCCF8091C00016
+:10EFA00080648093C0008BBEF3012791C65DDE4F65
+:10EFB0002883CA52D140A22EBB24CC24DD2408943D
+:10EFC000611C711C811C911C8BBEF3018791282E42
+:10EFD0003324442455240894611C711C811C911C09
+:10EFE0008BBEF3013791C55DDE4F3883CB52D140E4
+:10EFF0000894611C711C811C911C8BBEF30147910C
+:10F00000C45DDE4F4883CC52D140ADEFEA2EAFEF66
+:10F01000FA2EAFEF0A2FAFEF1A2F6E0C7F1C801E57
+:10F02000911E142D032DF22CEE24EA0CFB1C0C1D5A
+:10F030001D1D0F940DF220E22093C6008091C000A8
+:10F0400086FFFCCF8091C00080648093C000C65DC5
+:10F05000DE4F8881CA52D1400F940DF230E23093D6
+:10F06000C6008091C00086FFFCCF8091C000806404
+:10F070008093C000C45DDE4F8881CC52D1400F9494
+:10F080000DF240E24093C6008091C00086FFFCCFA5
+:10F090008091C00080648093C000C55DDE4F888190
+:10F0A000CB52D1400F940DF250E25093C6008091A4
+:10F0B000C00086FFFCCF8091C00080648093C000B8
+:10F0C0008FEFE8168FEFF80680E0080780E018075A
+:10F0D00031F484E092EE60E00F94C2F1DFC0D80119
+:10F0E000C7018070907CA070B0708050904CA040A0
+:10F0F000B040D1F52FEF3FE340E050E0E222F322B1
+:10F1000004231523CA5DDE4FA880B980CA80DB8046
+:10F11000C652D140AE0CBF1CC01ED11EAA0CBB1CD7
+:10F12000CC1CDD1C8EE092EE60E00F94C2F1BB2798
+:10F13000A12F902F8F2D0F940DF28E2D0F940DF285
+:10F1400030E23093C6008091C00086FFFCCF8091F2
+:10F15000C00080648093C0004EE34093C60080915D
+:10F16000C00086FFFCCF87C08EE09EEFA0E0B0E03D
+:10F17000E822F9220A231B239CE0E91694E9F90608
+:10F1800090E0090790E0190709F088C0C45DDE4FE0
+:10F19000A881CC52D140EA2EFF2400E010E0102FCD
+:10F1A0000F2DFE2CEE24C55DDE4FB881CB52D14031
+:10F1B000EB0EF11C011D111DD601C501817090706F
+:10F1C000A070B070DC0199278827E80EF91E0A1F8D
+:10F1D0001B1F20EF30E040E050E0A222B322C42207
+:10F1E000D52241E1AA0CBB1CCC1CDD1C4A95D1F7F1
+:10F1F000EA0CFB1C0C1D1D1D81E090E0A0E0B0E0BE
+:10F20000282239224A225B2235E1220C331C441C7D
+:10F21000551C3A95D1F7E20CF31C041D151D57013E
+:10F220006801AA0CBB1CCC1CDD1C85E192EE60E0E1
+:10F230000F94C2F1C801AA27BB270F940DF2BB2778
+:10F24000A12F902F8F2D0F940DF28E2D0F940DF274
+:10F2500090E29093C6008091C00086FFFCCF809121
+:10F26000C00080648093C000AEE3A093C60080918C
+:10F27000C00086FFFCCF8091C00080648093C000F6
+:10F28000C601AA27BB270F940DF2BB27AD2D9C2DDD
+:10F290008B2D0F940DF28A2D0F940DF20F94DCF14B
+:10F2A000CC5DDE4FE881F981C452D140F99709F471
+:10F2B0004DCBF4E0EF2EF12C012D112D6E0C7F1CA7
+:10F2C000801E911EF2CD83E093EE62E00F94F5F183
+:10F2D0008AE192EE60E00F94C2F18091C00087FF56
+:10F2E000FCCF1091C6001F751093C6008091C0001E
+:10F2F00086FFFCCF8091C00080648093C0000F9493
+:10F30000DCF1812F81548A3108F036C1163409F4BA
+:10F3100095C0173490F4133409F44EC0143430F40B
+:10F320001134F1F0123409F01DC130C0143409F465
+:10F3300059C0153409F016C16BC01A3409F4C4C0A1
+:10F340001B3438F4173409F48FC0183409F00AC19B
+:10F35000A1C01B3409F4D2C01C3409F003C1E8C0B9
+:10F360008FEF81B90DC082B1809582B980E090E0C5
+:10F37000E0EDF7E03197F1F70196883C9105C1F790
+:10F380008091C00087FFEFCF12B8EFC08FEF84B934
+:10F390000DC085B1809585B980E090E0E0EDF7E0A3
+:10F3A0003197F1F70196883C9105C1F78091C00033
+:10F3B00087FFEFCF15B8D9C08FEF87B90DC088B1DF
+:10F3C000809588B980E090E0E0EDF7E03197F1F7C3
+:10F3D0000196883C9105C1F78091C00087FFEFCF6F
+:10F3E00018B8C3C08FEF8AB90DC08BB180958BB9A7
+:10F3F00080E090E0E0EDF7E03197F1F70196883C8E
+:10F400009105C1F78091C00087FFEFCF1BB8ADC059
+:10F410008FEF8DB90DC08EB180958EB980E090E0F0
+:10F42000E0EDF7E03197F1F70196883C9105C1F7DF
+:10F430008091C00087FFEFCF1EB897C08FEF80BBD1
+:10F440000DC081B3809581BB80E090E0E0EDF7E0F6
+:10F450003197F1F70196883C9105C1F78091C00082
+:10F4600087FFEFCF11BA81C08FEF83BB0DC084B38C
+:10F47000809584BB80E090E0E0EDF7E03197F1F714
+:10F480000196883C9105C1F78091C00087FFEFCFBE
+:10F4900014BA6BC08FEF809301010FC080910201FD
+:10F4A00080958093020180E090E0E0EDF7E03197F5
+:10F4B000F1F70196883C9105C1F78091C00087FF64
+:10F4C000EDCF1092020151C08FEF809304010FC065
+:10F4D0008091050180958093050180E090E0E0ED4A
+:10F4E000F7E03197F1F70196883C9105C1F78091DB
+:10F4F000C00087FFEDCF1092050137C08FEF8093DA
+:10F5000007010FC08091080180958093080180E079
+:10F5100090E0E0EDF7E03197F1F70196883C910536
+:10F52000C1F78091C00087FFEDCF109208011DC088
+:10F530008FEF80930A010FC080910B01809580931B
+:10F540000B0180E090E0E0EDF7E03197F1F70196F4
+:10F55000883C9105C1F78091C00087FFEDCF1092E4
+:10F560000B0103C085E292EEEEC98091C00087FFD7
+:10F57000FCCF8091C600EAC988E392EEE4C98CE131
+:10F5800091EEE1C988249924933011F1943028F444
+:10F59000913089F09230B8F408C0953061F195301F
+:10F5A000F0F0963009F048C043C02B3109F042C951
+:10F5B00091E06BE13FC96227C15DDE4F2883CF52E6
+:10F5C000D14092E037C9B22FA0E0622793E032C960
+:10F5D000822F90E0A82BB92B622794E02BC92E3004
+:10F5E00009F039C3622795E0C05DDE4F19821882A9
+:10F5F000C053D1401FC9E1E0F0E0EC0FFD1FC05D3A
+:10F60000DE4F08811981C053D140E00FF11F2083E4
+:10F610000F5F1F4FC05DDE4F19830883C053D14079
+:10F6200062270A171B0709F005C9D80196E002C92D
+:10F63000261709F010C303C0973009F0FBC87724E0
+:10F640009981933109F412C19431C8F4963009F4C8
+:10F65000D8C0973050F4923009F406C1933009F4C1
+:10F660006DC0913009F059C253C0913109F477C08F
+:10F67000923108F0BBC0903109F04FC2F5C098310B
+:10F6800009F487C0993150F4953109F4EFC09531F0
+:10F6900008F4C6C1963109F040C2C2C19A3109F4DA
+:10F6A0006CC09A3108F491C09B3109F45BC09D3164
+:10F6B00009F033C29D81903359F48F81882311F46E
+:10F6C0009EE11CC0813011F091E018C098E916C08D
+:10F6D000892F807591F0903539F4E0E0F0E089E011
+:10F6E0008093570094910AC0983539F4E3E0F0E034
+:10F6F00089E080935700949101C090E01A821B82A8
+:10F700008D818C831D829E831F8227E030E009C299
+:10F710001A8288E08B8381E48C8386E58D8382E581
+:10F720008E8389E48F8383E5888780E589878FE5E9
+:10F730008A8782E38B872BE030E0F3C18A818139AD
+:10F7400041F0823941F0803911F48FE005C080E04A
+:10F7500003C082E001C08AE01A828B8344C0772410
+:10F76000739482C08D81882311F48EE12CC0813086
+:10F7700011F081E028C088E926C01A82E1E0F0E0BB
+:10F7800089E08093570084918B831C8224E030E0D1
+:10F79000C8C18B81803589F48C81883039F4E2E0EE
+:10F7A000F0E089E08093570084910DC0E0E0F0E044
+:10F7B00089E080935700849106C0E3E0F0E089E09F
+:10F7C0008093570084911A82DFCF8D81836C99E0FA
+:10F7D000E1E0F0E0082E90935700E89507B600FCB2
+:10F7E000FDCF1A821B8223E030E09BC180EC8A832C
+:10F7F000CE5CDE4F188219821A821B82C253D1401E
+:10F800008EC18A8190E0A0E0B0E0582F44273327D2
+:10F8100022278B8190E0A0E0B0E0DC0199278827C7
+:10F82000282B392B4A2B5B2B8D8190E0A0E0B0E098
+:10F83000282B392B4A2B5B2B8C8190E0A0E0B0E089
+:10F84000BA2FA92F982F8827282B392B4A2B5B2BCF
+:10F85000220F331F441F551FC05EDE4F288339839C
+:10F860004A835B83C052D1401A8259C13A81C95C34
+:10F87000DE4F3883C753D140CA5CDE4F1882C6536F
+:10F88000D1408B81C82EDD24CA5CDE4F488159816E
+:10F89000C653D140C42AD52A933109F082C0CE5C28
+:10F8A000DE4F88819981AA81BB81C253D1408050AB
+:10F8B000904CA340B04030F583E0CE5CDE4FE88052
+:10F8C000F9800A811B81C253D140F70100935B008C
+:10F8D00080935700E89507B600FCFDCFCE5CDE4F65
+:10F8E000088119812A813B81C253D14000501F4FAA
+:10F8F0002F4F3F4FCE5CDE4F088319832A833B8313
+:10F90000C253D140C05EDE4F488159816A817B81FC
+:10F91000C052D140DE011B9631E08C9111962C91A2
+:10F9200011971296C75CDE4F2883C953D140C85C3B
+:10F93000DE4F1882C853D14090E0C85CDE4FE881AA
+:10F94000F981C853D1408E2B9F2B0C01FA01609393
+:10F950005B0030935700E89511244E5F5F4F6F4F67
+:10F960007F4F0EEFE02E0FEFF02ECE0CDF1CC114F8
+:10F97000D10499F685E0C05EDE4F088119812A81A5
+:10F980003B81C052D140F80120935B008093570027
+:10F99000E89507B600FCFDCF81E180935700E8951C
+:10F9A00035C0C05EDE4F88819981AA81BB81C0527B
+:10F9B000D140B695A795979587957C018601ABE0D8
+:10F9C000AA2EB12CAC0EBD1E0BC0D5016D915D01F0
+:10F9D000C7010F947FFE0894E11CF11C01501040F8
+:10F9E0000115110591F7A60160E070E0440F551F65
+:10F9F000661F771FC05EDE4FE880F9800A811B8199
+:10FA0000C052D1404E0D5F1D601F711F1A82C05E33
+:10FA1000DE4F488359836A837B83C052D1407FC0C5
+:10FA2000FA80C55CDE4FF882CB53D140C65CDE4F16
+:10FA30001882CA53D1408B81C82EDD24C65CDE4FAC
+:10FA400008811981CA53D140C02AD12A1A828981DA
+:10FA5000BE016D5F7F4F843121F59601C05EDE4FA0
+:10FA6000E880F9800A811B81C052D1400BBFF701A9
+:10FA700087919691DB018C9311969C936E5F7F4FDB
+:10FA8000D801C7010296A11DB11DC05EDE4F88835B
+:10FA90009983AA83BB83C052D14022503040F1F6F3
+:10FAA00036C0C05EDE4F288139814A815B81C052F9
+:10FAB000D1400894C108D108760100E010E0089414
+:10FAC000C11CD11C0894E11CF11C011D111DE20E8A
+:10FAD000F31E041F151F21BDBB27A52F942F832FB5
+:10FAE00082BD2F5F3F4F4F4F5F4FF89A80B5DB01CC
+:10FAF0008D93BD012E153F054007510761F7C05E8C
+:10FB0000DE4F288339834A835B83C052D1409601FC
+:10FB10002D5F3F4FFB01108204C080EC8A8322E0FE
+:10FB200030E08BE18093C6008091C00086FFFCCF5F
+:10FB30008091C00080648093C000C15DDE4FF88179
+:10FB4000CF52D140F093C6008091C00086FFFCCF19
+:10FB50008091C00080648093C000432F3093C60022
+:10FB60008091C00086FFFCCF8091C00080648093AC
+:10FB7000C000922F2093C6008091C00086FFFCCF6A
+:10FB80008091C00080648093C0008EE08093C600A6
+:10FB90008091C00086FFFCCF8091C000806480937C
+:10FBA000C00065E1C15DDE4FE880CF52D1406E25D7
+:10FBB00069276427FE01319610C090819093C6009A
+:10FBC0008091C00086FFFCCF31968091C000806498
+:10FBD0008093C0006927215030402115310569F715
+:10FBE0006093C6008091C00086FFFCCF8091C0006A
+:10FBF00080648093C00085B1805885B9772081F4F6
+:10FC0000C15DDE4F0881CF52D1400F5FC15DDE4F35
+:10FC10000883CF52D14090E0A0E0B0E00D941AF4F8
+:10FC200027982F9880E090E020ED37E0F901319798
+:10FC3000F1F7019684369105C9F700008091C00064
+:10FC40008D7F8093C00081E180935700E895EE2777
+:10FC5000FF270994FFCF90E00D941AF497FB092E2B
+:10FC600007260AD077FD04D02ED006D000201AF443
+:10FC7000709561957F4F0895F6F7909581959F4F08
+:10FC80000895A1E21A2EAA1BBB1BFD010DC0AA1FDD
+:10FC9000BB1FEE1FFF1FA217B307E407F50720F0F5
+:10FCA000A21BB30BE40BF50B661F771F881F991F70
+:10FCB0001A9469F760957095809590959B01AC01B9
+:10FCC000BD01CF010895AA1BBB1B51E107C0AA1FAC
+:10FCD000BB1FA617B70710F0A61BB70B881F991FED
+:10FCE0005A95A9F780959095BC01CD010895F99991
+:10FCF000FECF92BD81BDF89A992780B50895262F31
+:10FD0000F999FECF1FBA92BD81BD20BD0FB6F89400
+:0EFD1000FA9AF99A0FBE01960895F894FFCF63
+:040000033000E000E9
+:00000001FF
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/SPI.cpp b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/SPI.cpp
new file mode 100644
index 00000000000..af14e07b12f
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/SPI.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2010 by Cristian Maglie <c.maglie@arduino.cc>
+ * Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API)
+ * Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings AVR)
+ * Copyright (c) 2014 by Andrew J. Kroll <xxxajk@gmail.com> (atomicity fixes)
+ * SPI Master library for arduino.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either the GNU General Public License version 2
+ * or the GNU Lesser General Public License version 2.1, both as
+ * published by the Free Software Foundation.
+ */
+
+#include "SPI.h"
+
+SPIClass SPI;
+
+uint8_t SPIClass::initialized = 0;
+uint8_t SPIClass::interruptMode = 0;
+uint8_t SPIClass::interruptMask = 0;
+uint8_t SPIClass::interruptSave = 0;
+#ifdef SPI_TRANSACTION_MISMATCH_LED
+uint8_t SPIClass::inTransactionFlag = 0;
+#endif
+
+void SPIClass::begin()
+{
+  uint8_t sreg = SREG;
+  noInterrupts(); // Protect from a scheduler and prevent transactionBegin
+  if (!initialized) {
+    // Set SS to high so a connected chip will be "deselected" by default
+    uint8_t port = digitalPinToPort(SS);
+    uint8_t bit = digitalPinToBitMask(SS);
+    volatile uint8_t *reg = portModeRegister(port);
+
+    // if the SS pin is not already configured as an output
+    // then set it high (to enable the internal pull-up resistor)
+    if(!(*reg & bit)){
+      digitalWrite(SS, HIGH);
+    }
+
+    // When the SS pin is set as OUTPUT, it can be used as
+    // a general purpose output port (it doesn't influence
+    // SPI operations).
+    pinMode(SS, OUTPUT);
+
+    // Warning: if the SS pin ever becomes a LOW INPUT then SPI
+    // automatically switches to Slave, so the data direction of
+    // the SS pin MUST be kept as OUTPUT.
+    SPCR |= _BV(MSTR);
+    SPCR |= _BV(SPE);
+
+    // Set direction register for SCK and MOSI pin.
+    // MISO pin automatically overrides to INPUT.
+    // By doing this AFTER enabling SPI, we avoid accidentally
+    // clocking in a single bit since the lines go directly
+    // from "input" to SPI control.
+    // http://code.google.com/p/arduino/issues/detail?id=888
+    pinMode(SCK, OUTPUT);
+    pinMode(MOSI, OUTPUT);
+  }
+  initialized++; // reference count
+  SREG = sreg;
+}
+
+void SPIClass::end() {
+  uint8_t sreg = SREG;
+  noInterrupts(); // Protect from a scheduler and prevent transactionBegin
+  // Decrease the reference counter
+  if (initialized)
+    initialized--;
+  // If there are no more references disable SPI
+  if (!initialized) {
+    SPCR &= ~_BV(SPE);
+    interruptMode = 0;
+    #ifdef SPI_TRANSACTION_MISMATCH_LED
+    inTransactionFlag = 0;
+    #endif
+  }
+  SREG = sreg;
+}
+
+// mapping of interrupt numbers to bits within SPI_AVR_EIMSK
+#if defined(__AVR_ATmega32U4__)
+  #define SPI_INT0_MASK  (1<<INT0)
+  #define SPI_INT1_MASK  (1<<INT1)
+  #define SPI_INT2_MASK  (1<<INT2)
+  #define SPI_INT3_MASK  (1<<INT3)
+  #define SPI_INT4_MASK  (1<<INT6)
+#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
+  #define SPI_INT0_MASK  (1<<INT0)
+  #define SPI_INT1_MASK  (1<<INT1)
+  #define SPI_INT2_MASK  (1<<INT2)
+  #define SPI_INT3_MASK  (1<<INT3)
+  #define SPI_INT4_MASK  (1<<INT4)
+  #define SPI_INT5_MASK  (1<<INT5)
+  #define SPI_INT6_MASK  (1<<INT6)
+  #define SPI_INT7_MASK  (1<<INT7)
+#elif defined(EICRA) && defined(EICRB) && defined(EIMSK)
+  #define SPI_INT0_MASK  (1<<INT4)
+  #define SPI_INT1_MASK  (1<<INT5)
+  #define SPI_INT2_MASK  (1<<INT0)
+  #define SPI_INT3_MASK  (1<<INT1)
+  #define SPI_INT4_MASK  (1<<INT2)
+  #define SPI_INT5_MASK  (1<<INT3)
+  #define SPI_INT6_MASK  (1<<INT6)
+  #define SPI_INT7_MASK  (1<<INT7)
+#else
+  #ifdef INT0
+  #define SPI_INT0_MASK  (1<<INT0)
+  #endif
+  #ifdef INT1
+  #define SPI_INT1_MASK  (1<<INT1)
+  #endif
+  #ifdef INT2
+  #define SPI_INT2_MASK  (1<<INT2)
+  #endif
+#endif
+
+void SPIClass::usingInterrupt(uint8_t interruptNumber)
+{
+  uint8_t mask = 0;
+  uint8_t sreg = SREG;
+  noInterrupts(); // Protect from a scheduler and prevent transactionBegin
+  switch (interruptNumber) {
+  #ifdef SPI_INT0_MASK
+  case 0: mask = SPI_INT0_MASK; break;
+  #endif
+  #ifdef SPI_INT1_MASK
+  case 1: mask = SPI_INT1_MASK; break;
+  #endif
+  #ifdef SPI_INT2_MASK
+  case 2: mask = SPI_INT2_MASK; break;
+  #endif
+  #ifdef SPI_INT3_MASK
+  case 3: mask = SPI_INT3_MASK; break;
+  #endif
+  #ifdef SPI_INT4_MASK
+  case 4: mask = SPI_INT4_MASK; break;
+  #endif
+  #ifdef SPI_INT5_MASK
+  case 5: mask = SPI_INT5_MASK; break;
+  #endif
+  #ifdef SPI_INT6_MASK
+  case 6: mask = SPI_INT6_MASK; break;
+  #endif
+  #ifdef SPI_INT7_MASK
+  case 7: mask = SPI_INT7_MASK; break;
+  #endif
+  default:
+    interruptMode = 2;
+    break;
+  }
+  interruptMask |= mask;
+  if (!interruptMode)
+    interruptMode = 1;
+  SREG = sreg;
+}
+
+void SPIClass::notUsingInterrupt(uint8_t interruptNumber)
+{
+  // Once in mode 2 we can't go back to 0 without a proper reference count
+  if (interruptMode == 2)
+    return;
+  uint8_t mask = 0;
+  uint8_t sreg = SREG;
+  noInterrupts(); // Protect from a scheduler and prevent transactionBegin
+  switch (interruptNumber) {
+  #ifdef SPI_INT0_MASK
+  case 0: mask = SPI_INT0_MASK; break;
+  #endif
+  #ifdef SPI_INT1_MASK
+  case 1: mask = SPI_INT1_MASK; break;
+  #endif
+  #ifdef SPI_INT2_MASK
+  case 2: mask = SPI_INT2_MASK; break;
+  #endif
+  #ifdef SPI_INT3_MASK
+  case 3: mask = SPI_INT3_MASK; break;
+  #endif
+  #ifdef SPI_INT4_MASK
+  case 4: mask = SPI_INT4_MASK; break;
+  #endif
+  #ifdef SPI_INT5_MASK
+  case 5: mask = SPI_INT5_MASK; break;
+  #endif
+  #ifdef SPI_INT6_MASK
+  case 6: mask = SPI_INT6_MASK; break;
+  #endif
+  #ifdef SPI_INT7_MASK
+  case 7: mask = SPI_INT7_MASK; break;
+  #endif
+  default:
+    break;
+    // this case can't be reached
+  }
+  interruptMask &= ~mask;
+  if (!interruptMask)
+    interruptMode = 0;
+  SREG = sreg;
+}
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/SPI.h b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/SPI.h
new file mode 100644
index 00000000000..5206a091843
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/SPI.h
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2010 by Cristian Maglie <c.maglie@arduino.cc>
+ * Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API)
+ * Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings AVR)
+ * Copyright (c) 2014 by Andrew J. Kroll <xxxajk@gmail.com> (atomicity fixes)
+ * SPI Master library for arduino.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either the GNU General Public License version 2
+ * or the GNU Lesser General Public License version 2.1, both as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _SPI_H_INCLUDED
+#define _SPI_H_INCLUDED
+
+#include <Arduino.h>
+
+// SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(),
+// usingInterrupt(), and SPISetting(clock, bitOrder, dataMode)
+#define SPI_HAS_TRANSACTION 1
+
+// SPI_HAS_NOTUSINGINTERRUPT means that SPI has notUsingInterrupt() method
+#define SPI_HAS_NOTUSINGINTERRUPT 1
+
+// SPI_ATOMIC_VERSION means that SPI has atomicity fixes and what version.
+// This way when there is a bug fix you can check this define to alert users
+// of your code if it uses better version of this library.
+// This also implies everything that SPI_HAS_TRANSACTION as documented above is
+// available too.
+#define SPI_ATOMIC_VERSION 1
+
+// Uncomment this line to add detection of mismatched begin/end transactions.
+// A mismatch occurs if other libraries fail to use SPI.endTransaction() for
+// each SPI.beginTransaction().  Connect an LED to this pin.  The LED will turn
+// on if any mismatch is ever detected.
+//#define SPI_TRANSACTION_MISMATCH_LED 5
+
+#ifndef LSBFIRST
+#define LSBFIRST 0
+#endif
+#ifndef MSBFIRST
+#define MSBFIRST 1
+#endif
+
+#define SPI_CLOCK_DIV4 0x00
+#define SPI_CLOCK_DIV16 0x01
+#define SPI_CLOCK_DIV64 0x02
+#define SPI_CLOCK_DIV128 0x03
+#define SPI_CLOCK_DIV2 0x04
+#define SPI_CLOCK_DIV8 0x05
+#define SPI_CLOCK_DIV32 0x06
+
+#define SPI_MODE0 0x00
+#define SPI_MODE1 0x04
+#define SPI_MODE2 0x08
+#define SPI_MODE3 0x0C
+
+#define SPI_MODE_MASK 0x0C  // CPOL = bit 3, CPHA = bit 2 on SPCR
+#define SPI_CLOCK_MASK 0x03  // SPR1 = bit 1, SPR0 = bit 0 on SPCR
+#define SPI_2XCLOCK_MASK 0x01  // SPI2X = bit 0 on SPSR
+
+// define SPI_AVR_EIMSK for AVR boards with external interrupt pins
+#if defined(EIMSK)
+  #define SPI_AVR_EIMSK  EIMSK
+#elif defined(GICR)
+  #define SPI_AVR_EIMSK  GICR
+#elif defined(GIMSK)
+  #define SPI_AVR_EIMSK  GIMSK
+#endif
+
+class SPISettings {
+public:
+  SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
+    if (__builtin_constant_p(clock)) {
+      init_AlwaysInline(clock, bitOrder, dataMode);
+    } else {
+      init_MightInline(clock, bitOrder, dataMode);
+    }
+  }
+  SPISettings() {
+    init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0);
+  }
+private:
+  void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
+    init_AlwaysInline(clock, bitOrder, dataMode);
+  }
+  void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
+    __attribute__((__always_inline__)) {
+    // Clock settings are defined as follows. Note that this shows SPI2X
+    // inverted, so the bits form increasing numbers. Also note that
+    // fosc/64 appears twice
+    // SPR1 SPR0 ~SPI2X Freq
+    //   0    0     0   fosc/2
+    //   0    0     1   fosc/4
+    //   0    1     0   fosc/8
+    //   0    1     1   fosc/16
+    //   1    0     0   fosc/32
+    //   1    0     1   fosc/64
+    //   1    1     0   fosc/64
+    //   1    1     1   fosc/128
+
+    // We find the fastest clock that is less than or equal to the
+    // given clock rate. The clock divider that results in clock_setting
+    // is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the
+    // slowest (128 == 2 ^^ 7, so clock_div = 6).
+    uint8_t clockDiv;
+
+    // When the clock is known at compiletime, use this if-then-else
+    // cascade, which the compiler knows how to completely optimize
+    // away. When clock is not known, use a loop instead, which generates
+    // shorter code.
+    if (__builtin_constant_p(clock)) {
+      if (clock >= F_CPU / 2) {
+        clockDiv = 0;
+      } else if (clock >= F_CPU / 4) {
+        clockDiv = 1;
+      } else if (clock >= F_CPU / 8) {
+        clockDiv = 2;
+      } else if (clock >= F_CPU / 16) {
+        clockDiv = 3;
+      } else if (clock >= F_CPU / 32) {
+        clockDiv = 4;
+      } else if (clock >= F_CPU / 64) {
+        clockDiv = 5;
+      } else {
+        clockDiv = 6;
+      }
+    } else {
+      uint32_t clockSetting = F_CPU / 2;
+      clockDiv = 0;
+      while (clockDiv < 6 && clock < clockSetting) {
+        clockSetting /= 2;
+        clockDiv++;
+      }
+    }
+
+    // Compensate for the duplicate fosc/64
+    if (clockDiv == 6)
+    clockDiv = 7;
+
+    // Invert the SPI2X bit
+    clockDiv ^= 0x1;
+
+    // Pack into the SPISettings class
+    spcr = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) |
+      (dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK);
+    spsr = clockDiv & SPI_2XCLOCK_MASK;
+  }
+  uint8_t spcr;
+  uint8_t spsr;
+  friend class SPIClass;
+};
+
+
+class SPIClass {
+public:
+  // Initialize the SPI library
+  static void begin();
+
+  // If SPI is used from within an interrupt, this function registers
+  // that interrupt with the SPI library, so beginTransaction() can
+  // prevent conflicts.  The input interruptNumber is the number used
+  // with attachInterrupt.  If SPI is used from a different interrupt
+  // (eg, a timer), interruptNumber should be 255.
+  static void usingInterrupt(uint8_t interruptNumber);
+  // And this does the opposite.
+  static void notUsingInterrupt(uint8_t interruptNumber);
+  // Note: the usingInterrupt and notUsingInterrupt functions should
+  // not to be called from ISR context or inside a transaction.
+  // For details see:
+  // https://github.com/arduino/Arduino/pull/2381
+  // https://github.com/arduino/Arduino/pull/2449
+
+  // Before using SPI.transfer() or asserting chip select pins,
+  // this function is used to gain exclusive access to the SPI bus
+  // and configure the correct settings.
+  inline static void beginTransaction(SPISettings settings) {
+    if (interruptMode > 0) {
+      uint8_t sreg = SREG;
+      noInterrupts();
+
+      #ifdef SPI_AVR_EIMSK
+      if (interruptMode == 1) {
+        interruptSave = SPI_AVR_EIMSK;
+        SPI_AVR_EIMSK &= ~interruptMask;
+        SREG = sreg;
+      } else
+      #endif
+      {
+        interruptSave = sreg;
+      }
+    }
+
+    #ifdef SPI_TRANSACTION_MISMATCH_LED
+    if (inTransactionFlag) {
+      pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
+      digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
+    }
+    inTransactionFlag = 1;
+    #endif
+
+    SPCR = settings.spcr;
+    SPSR = settings.spsr;
+  }
+
+  // Write to the SPI bus (MOSI pin) and also receive (MISO pin)
+  inline static uint8_t transfer(uint8_t data) {
+    SPDR = data;
+    /*
+     * The following NOP introduces a small delay that can prevent the wait
+     * loop form iterating when running at the maximum speed. This gives
+     * about 10% more speed, even if it seems counter-intuitive. At lower
+     * speeds it is unnoticed.
+     */
+    asm volatile("nop");
+    while (!(SPSR & _BV(SPIF))) ; // wait
+    return SPDR;
+  }
+  inline static uint16_t transfer16(uint16_t data) {
+    union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } in, out;
+    in.val = data;
+    if (!(SPCR & _BV(DORD))) {
+      SPDR = in.msb;
+      asm volatile("nop"); // See transfer(uint8_t) function
+      while (!(SPSR & _BV(SPIF))) ;
+      out.msb = SPDR;
+      SPDR = in.lsb;
+      asm volatile("nop");
+      while (!(SPSR & _BV(SPIF))) ;
+      out.lsb = SPDR;
+    } else {
+      SPDR = in.lsb;
+      asm volatile("nop");
+      while (!(SPSR & _BV(SPIF))) ;
+      out.lsb = SPDR;
+      SPDR = in.msb;
+      asm volatile("nop");
+      while (!(SPSR & _BV(SPIF))) ;
+      out.msb = SPDR;
+    }
+    return out.val;
+  }
+  inline static void transfer(void *buf, size_t count) {
+    if (count == 0) return;
+    uint8_t *p = (uint8_t *)buf;
+    SPDR = *p;
+    while (--count > 0) {
+      uint8_t out = *(p + 1);
+      while (!(SPSR & _BV(SPIF))) ;
+      uint8_t in = SPDR;
+      SPDR = out;
+      *p++ = in;
+    }
+    while (!(SPSR & _BV(SPIF))) ;
+    *p = SPDR;
+  }
+  // After performing a group of transfers and releasing the chip select
+  // signal, this function allows others to access the SPI bus
+  inline static void endTransaction(void) {
+    #ifdef SPI_TRANSACTION_MISMATCH_LED
+    if (!inTransactionFlag) {
+      pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
+      digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
+    }
+    inTransactionFlag = 0;
+    #endif
+
+    if (interruptMode > 0) {
+      #ifdef SPI_AVR_EIMSK
+      uint8_t sreg = SREG;
+      #endif
+      noInterrupts();
+      #ifdef SPI_AVR_EIMSK
+      if (interruptMode == 1) {
+        SPI_AVR_EIMSK = interruptSave;
+        SREG = sreg;
+      } else
+      #endif
+      {
+        SREG = interruptSave;
+      }
+    }
+  }
+
+  // Disable the SPI bus
+  static void end();
+
+  // This function is deprecated.  New applications should use
+  // beginTransaction() to configure SPI settings.
+  inline static void setBitOrder(uint8_t bitOrder) {
+    if (bitOrder == LSBFIRST) SPCR |= _BV(DORD);
+    else SPCR &= ~(_BV(DORD));
+  }
+  // This function is deprecated.  New applications should use
+  // beginTransaction() to configure SPI settings.
+  inline static void setDataMode(uint8_t dataMode) {
+    SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode;
+  }
+  // This function is deprecated.  New applications should use
+  // beginTransaction() to configure SPI settings.
+  inline static void setClockDivider(uint8_t clockDiv) {
+    SPCR = (SPCR & ~SPI_CLOCK_MASK) | (clockDiv & SPI_CLOCK_MASK);
+    SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((clockDiv >> 2) & SPI_2XCLOCK_MASK);
+  }
+  // These undocumented functions should not be used.  SPI.transfer()
+  // polls the hardware flag which is automatically cleared as the
+  // AVR responds to SPI's interrupt
+  inline static void attachInterrupt() { SPCR |= _BV(SPIE); }
+  inline static void detachInterrupt() { SPCR &= ~_BV(SPIE); }
+
+private:
+  static uint8_t initialized;
+  static uint8_t interruptMode; // 0=none, 1=mask, 2=global
+  static uint8_t interruptMask; // which interrupts to mask
+  static uint8_t interruptSave; // temp storage, to restore state
+  #ifdef SPI_TRANSACTION_MISMATCH_LED
+  static uint8_t inTransactionFlag;
+  #endif
+};
+
+extern SPIClass SPI;
+
+#endif
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/examples/BarometricPressureSensor/BarometricPressureSensor.ino b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/examples/BarometricPressureSensor/BarometricPressureSensor.ino
new file mode 100644
index 00000000000..8104fcbc252
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/examples/BarometricPressureSensor/BarometricPressureSensor.ino
@@ -0,0 +1,143 @@
+/*
+ SCP1000 Barometric Pressure Sensor Display
+
+ Shows the output of a Barometric Pressure Sensor on a
+ Uses the SPI library. For details on the sensor, see:
+ http://www.sparkfun.com/commerce/product_info.php?products_id=8161
+ http://www.vti.fi/en/support/obsolete_products/pressure_sensors/
+
+ This sketch adapted from Nathan Seidle's SCP1000 example for PIC:
+ http://www.sparkfun.com/datasheets/Sensors/SCP1000-Testing.zip
+
+ Circuit:
+ SCP1000 sensor attached to pins 6, 7, 10 - 13:
+ DRDY: pin 6
+ CSB: pin 7
+ MOSI: pin 11
+ MISO: pin 12
+ SCK: pin 13
+
+ created 31 July 2010
+ modified 14 August 2010
+ by Tom Igoe
+ */
+
+// the sensor communicates using SPI, so include the library:
+#include <SPI.h>
+
+//Sensor's memory register addresses:
+const int PRESSURE = 0x1F;      //3 most significant bits of pressure
+const int PRESSURE_LSB = 0x20;  //16 least significant bits of pressure
+const int TEMPERATURE = 0x21;   //16 bit temperature reading
+const byte READ = 0b11111100;     // SCP1000's read command
+const byte WRITE = 0b00000010;   // SCP1000's write command
+
+// pins used for the connection with the sensor
+// the other you need are controlled by the SPI library):
+const int dataReadyPin = 6;
+const int chipSelectPin = 7;
+
+void setup() {
+  Serial.begin(9600);
+
+  // start the SPI library:
+  SPI.begin();
+
+  // initalize the  data ready and chip select pins:
+  pinMode(dataReadyPin, INPUT);
+  pinMode(chipSelectPin, OUTPUT);
+
+  //Configure SCP1000 for low noise configuration:
+  writeRegister(0x02, 0x2D);
+  writeRegister(0x01, 0x03);
+  writeRegister(0x03, 0x02);
+  // give the sensor time to set up:
+  delay(100);
+}
+
+void loop() {
+  //Select High Resolution Mode
+  writeRegister(0x03, 0x0A);
+
+  // don't do anything until the data ready pin is high:
+  if (digitalRead(dataReadyPin) == HIGH) {
+    //Read the temperature data
+    int tempData = readRegister(0x21, 2);
+
+    // convert the temperature to celsius and display it:
+    float realTemp = (float)tempData / 20.0;
+    Serial.print("Temp[C]=");
+    Serial.print(realTemp);
+
+
+    //Read the pressure data highest 3 bits:
+    byte  pressure_data_high = readRegister(0x1F, 1);
+    pressure_data_high &= 0b00000111; //you only needs bits 2 to 0
+
+    //Read the pressure data lower 16 bits:
+    unsigned int pressure_data_low = readRegister(0x20, 2);
+    //combine the two parts into one 19-bit number:
+    long pressure = ((pressure_data_high << 16) | pressure_data_low) / 4;
+
+    // display the temperature:
+    Serial.println("\tPressure [Pa]=" + String(pressure));
+  }
+}
+
+//Read from or write to register from the SCP1000:
+unsigned int readRegister(byte thisRegister, int bytesToRead ) {
+  byte inByte = 0;           // incoming byte from the SPI
+  unsigned int result = 0;   // result to return
+  Serial.print(thisRegister, BIN);
+  Serial.print("\t");
+  // SCP1000 expects the register name in the upper 6 bits
+  // of the byte. So shift the bits left by two bits:
+  thisRegister = thisRegister << 2;
+  // now combine the address and the command into one byte
+  byte dataToSend = thisRegister & READ;
+  Serial.println(thisRegister, BIN);
+  // take the chip select low to select the device:
+  digitalWrite(chipSelectPin, LOW);
+  // send the device the register you want to read:
+  SPI.transfer(dataToSend);
+  // send a value of 0 to read the first byte returned:
+  result = SPI.transfer(0x00);
+  // decrement the number of bytes left to read:
+  bytesToRead--;
+  // if you still have another byte to read:
+  if (bytesToRead > 0) {
+    // shift the first byte left, then get the second byte:
+    result = result << 8;
+    inByte = SPI.transfer(0x00);
+    // combine the byte you just got with the previous one:
+    result = result | inByte;
+    // decrement the number of bytes left to read:
+    bytesToRead--;
+  }
+  // take the chip select high to de-select:
+  digitalWrite(chipSelectPin, HIGH);
+  // return the result:
+  return(result);
+}
+
+
+//Sends a write command to SCP1000
+
+void writeRegister(byte thisRegister, byte thisValue) {
+
+  // SCP1000 expects the register address in the upper 6 bits
+  // of the byte. So shift the bits left by two bits:
+  thisRegister = thisRegister << 2;
+  // now combine the register address and the command into one byte:
+  byte dataToSend = thisRegister | WRITE;
+
+  // take the chip select low to select the device:
+  digitalWrite(chipSelectPin, LOW);
+
+  SPI.transfer(dataToSend); //Send register location
+  SPI.transfer(thisValue);  //Send value to record into register
+
+  // take the chip select high to de-select:
+  digitalWrite(chipSelectPin, HIGH);
+}
+
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/examples/DigitalPotControl/DigitalPotControl.ino b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/examples/DigitalPotControl/DigitalPotControl.ino
new file mode 100644
index 00000000000..b135a74f4ed
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/examples/DigitalPotControl/DigitalPotControl.ino
@@ -0,0 +1,71 @@
+/*
+  Digital Pot Control
+
+  This example controls an Analog Devices AD5206 digital potentiometer.
+  The AD5206 has 6 potentiometer channels. Each channel's pins are labeled
+  A - connect this to voltage
+  W - this is the pot's wiper, which changes when you set it
+  B - connect this to ground.
+
+ The AD5206 is SPI-compatible,and to command it, you send two bytes,
+ one with the channel number (0 - 5) and one with the resistance value for the
+ channel (0 - 255).
+
+ The circuit:
+  * All A pins  of AD5206 connected to +5V
+  * All B pins of AD5206 connected to ground
+  * An LED and a 220-ohm resisor in series connected from each W pin to ground
+  * CS - to digital pin 10  (SS pin)
+  * SDI - to digital pin 11 (MOSI pin)
+  * CLK - to digital pin 13 (SCK pin)
+
+ created 10 Aug 2010
+ by Tom Igoe
+
+ Thanks to Heather Dewey-Hagborg for the original tutorial, 2005
+
+*/
+
+
+// inslude the SPI library:
+#include <SPI.h>
+
+
+// set pin 10 as the slave select for the digital pot:
+const int slaveSelectPin = 10;
+
+void setup() {
+  // set the slaveSelectPin as an output:
+  pinMode (slaveSelectPin, OUTPUT);
+  // initialize SPI:
+  SPI.begin();
+}
+
+void loop() {
+  // go through the six channels of the digital pot:
+  for (int channel = 0; channel < 6; channel++) {
+    // change the resistance on this channel from min to max:
+    for (int level = 0; level < 255; level++) {
+      digitalPotWrite(channel, level);
+      delay(10);
+    }
+    // wait a second at the top:
+    delay(100);
+    // change the resistance on this channel from max to min:
+    for (int level = 0; level < 255; level++) {
+      digitalPotWrite(channel, 255 - level);
+      delay(10);
+    }
+  }
+
+}
+
+void digitalPotWrite(int address, int value) {
+  // take the SS pin low to select the chip:
+  digitalWrite(slaveSelectPin, LOW);
+  //  send in the address and value via SPI:
+  SPI.transfer(address);
+  SPI.transfer(value);
+  // take the SS pin high to de-select the chip:
+  digitalWrite(slaveSelectPin, HIGH);
+}
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/keywords.txt b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/keywords.txt
new file mode 100644
index 00000000000..fa7616581aa
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/keywords.txt
@@ -0,0 +1,36 @@
+#######################################
+# Syntax Coloring Map SPI
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+SPI	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+begin	KEYWORD2
+end	KEYWORD2
+transfer	KEYWORD2
+setBitOrder	KEYWORD2
+setDataMode	KEYWORD2
+setClockDivider	KEYWORD2
+
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+SPI_CLOCK_DIV4	LITERAL1
+SPI_CLOCK_DIV16	LITERAL1
+SPI_CLOCK_DIV64	LITERAL1
+SPI_CLOCK_DIV128	LITERAL1
+SPI_CLOCK_DIV2	LITERAL1
+SPI_CLOCK_DIV8	LITERAL1
+SPI_CLOCK_DIV32	LITERAL1
+SPI_CLOCK_DIV64	LITERAL1
+SPI_MODE0	LITERAL1
+SPI_MODE1	LITERAL1
+SPI_MODE2	LITERAL1
+SPI_MODE3	LITERAL1
\ No newline at end of file
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/library.properties b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/library.properties
new file mode 100644
index 00000000000..07af8696109
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/libraries/SPI/library.properties
@@ -0,0 +1,10 @@
+name=SPI
+version=1.0
+author=Arduino
+maintainer=Arduino <info@arduino.cc>
+sentence=Enables the communication with devices that use the Serial Peripheral Interface (SPI) Bus. For all Arduino boards, BUT Arduino DUE. 
+paragraph=
+url=http://arduino.cc/en/Reference/SPI
+architectures=avr
+types=Arduino
+
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/platform.txt b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/platform.txt
new file mode 100644
index 00000000000..cc0be0a0c5f
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/my_avr_platform/avr/platform.txt
@@ -0,0 +1,9 @@
+
+# Arduino AVR Core and platform.
+# ------------------------------
+
+# For more info:
+# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification
+
+name=My AVR Boards
+version=9.9.9
\ No newline at end of file
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/watterott/avr/boards.txt b/arduino/cores/packagemanager/testdata/custom_hardware/watterott/avr/boards.txt
new file mode 100644
index 00000000000..be844787844
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/watterott/avr/boards.txt
@@ -0,0 +1,130 @@
+# VID 0x6666 is a prototype product Vendor ID
+# http://www.linux-usb.org/usb.ids
+
+menu.speed=Speed
+menu.core=Core
+menu.info=Info
+
+
+# ATmega32u4 @ 16 MHz
+atmega32u4.name=ATmega32u4
+atmega32u4.menu.speed.16mhz=16 MHz
+atmega32u4.menu.speed.16mhz.build.f_cpu=16000000L
+atmega32u4.menu.speed.16mhz.bootloader.file=caterina_16mhz.hex
+atmega32u4.menu.speed.8mhz=8 MHz
+atmega32u4.menu.speed.8mhz.build.f_cpu=8000000L
+atmega32u4.menu.speed.8mhz.bootloader.file=caterina_8mhz.hex
+atmega32u4.vid.0=0x1D50
+atmega32u4.pid.0=0x60B0
+atmega32u4.vid.1=0x6666
+atmega32u4.pid.1=0x60B0
+atmega32u4.vid.2=0x2341
+atmega32u4.pid.2=0x0036
+atmega32u4.vid.3=0x2341
+atmega32u4.pid.3=0x8036
+atmega32u4.vid.4=0x2A03
+atmega32u4.pid.4=0x0036
+atmega32u4.vid.5=0x2A03
+atmega32u4.pid.5=0x8036
+atmega32u4.bootloader.tool=avrdude
+atmega32u4.bootloader.low_fuses=0xff
+atmega32u4.bootloader.high_fuses=0xd8
+atmega32u4.bootloader.extended_fuses=0xcb
+#atmega32u4.bootloader.file=caterina_16mhz.hex
+atmega32u4.bootloader.unlock_bits=0x3F
+atmega32u4.bootloader.lock_bits=0x2F
+atmega32u4.upload.tool=avrdude
+atmega32u4.upload.protocol=avr109
+atmega32u4.upload.maximum_size=28672
+atmega32u4.upload.maximum_data_size=2560
+atmega32u4.upload.speed=57600
+atmega32u4.upload.disable_flushing=true
+atmega32u4.upload.use_1200bps_touch=true
+atmega32u4.upload.wait_for_upload_port=true
+atmega32u4.build.mcu=atmega32u4
+#atmega32u4.build.f_cpu=16000000L
+atmega32u4.build.vid=0x6666
+atmega32u4.build.pid=0x60B0
+atmega32u4.build.usb_product="USB IO Board"
+atmega32u4.build.usb_manufacturer="ATmega32u4"
+atmega32u4.build.board=AVR_LEONARDO
+atmega32u4.build.core=arduino:arduino
+atmega32u4.build.variant=leonardo
+atmega32u4.build.extra_flags={build.usb_flags} -DMOUSE_ABS_ENABLED
+
+
+# ATtiny841 @ internal 8 MHz
+attiny841.name=ATtiny841 (8 MHz)
+# use Standard Arduino Core
+attiny841.menu.core.arduino=Standard Arduino
+attiny841.menu.core.arduino.build.core=arduino:arduino
+attiny841.menu.core.arduino.build.variant=tiny14
+# use Spence Konde Core: https://github.com/SpenceKonde/arduino-tiny-841/
+attiny841.menu.core.spencekonde=ATtiny841 (by Spence Konde)
+#attiny841.menu.core.spencekonde.build.core=arduino-tiny-841:tiny
+attiny841.menu.core.spencekonde.build.core=tiny841
+attiny841.menu.core.spencekonde.build.variant=tiny14
+# info menu item
+attiny841.menu.info.info=Press Reset, when Uploading is shown.
+attiny841.vid.0=0x16D0
+attiny841.pid.0=0x0753
+attiny841.bootloader.tool=avrdude
+attiny841.bootloader.low_fuses=0xE2
+attiny841.bootloader.high_fuses=0xDD
+attiny841.bootloader.extended_fuses=0xFE
+attiny841.bootloader.unlock_bits=0xFF
+attiny841.bootloader.lock_bits=0xFF
+attiny841.bootloader.file=micronucleus-t841.hex
+attiny841.upload.tool=micronucleus
+attiny841.upload.protocol=usb
+attiny841.upload.wait_for_upload_port=false
+attiny841.upload.use_1200bps_touch=false
+attiny841.upload.disable_flushing=false
+attiny841.upload.maximum_size=6500
+attiny841.build.mcu=attiny841
+attiny841.build.f_cpu=8000000L
+attiny841.build.board=AVR_ATTINY841
+#attiny841.build.core=arduino:arduino
+#attiny841.build.variant=tiny14
+
+
+# ATtiny85 @ internal 16.5 MHz
+attiny85.name=ATtiny85 (16.5 MHz)
+# use Standard Arduino Core
+attiny85.menu.core.arduino=Standard Arduino
+attiny85.menu.core.arduino.build.board=AVR_ATTINY85
+attiny85.menu.core.arduino.build.core=arduino:arduino
+attiny85.menu.core.arduino.build.variant=tiny8
+# use Spence Konde Core: https://github.com/SpenceKonde/ATTinyCore
+attiny85.menu.core.spencekonde=ATtiny85 (by Spence Konde)
+attiny85.menu.core.spencekonde.build.board=AVR_ATTINY85
+#attiny85.menu.core.spencekonde.build.core=ATTinyCore:tiny
+attiny85.menu.core.spencekonde.build.core=tiny85
+attiny85.menu.core.spencekonde.build.variant=tiny8
+# use Digistump Core: https://github.com/digistump/DigistumpArduino
+attiny85.menu.core.digistump=Digistump/Digispark
+attiny85.menu.core.digistump.build.board=AVR_DIGISPARK
+attiny85.menu.core.digistump.build.core=digistump:tiny
+attiny85.menu.core.digistump.build.variant=digispark
+# info menu item
+attiny85.menu.info.info=Press Reset, when Uploading is shown.
+attiny85.vid.0=0x16D0
+attiny85.pid.0=0x0753
+attiny85.bootloader.tool=avrdude
+attiny85.bootloader.low_fuses=0xE1
+attiny85.bootloader.high_fuses=0xDD
+attiny85.bootloader.extended_fuses=0xFE
+attiny85.bootloader.unlock_bits=0xFF
+attiny85.bootloader.lock_bits=0xFF
+attiny85.bootloader.file=micronucleus-t85.hex
+attiny85.upload.tool=micronucleus
+attiny85.upload.protocol=usb
+attiny85.upload.wait_for_upload_port=false
+attiny85.upload.use_1200bps_touch=false
+attiny85.upload.disable_flushing=false
+attiny85.upload.maximum_size=6300
+attiny85.build.mcu=attiny85
+attiny85.build.f_cpu=16500000L
+attiny85.build.board=AVR_ATTINY85
+#attiny85.build.core=arduino:arduino
+#attiny85.build.variant=tiny8
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/watterott/avr/platform.txt b/arduino/cores/packagemanager/testdata/custom_hardware/watterott/avr/platform.txt
new file mode 100644
index 00000000000..13d46ceae1e
--- /dev/null
+++ b/arduino/cores/packagemanager/testdata/custom_hardware/watterott/avr/platform.txt
@@ -0,0 +1,146 @@
+
+# Watterott AVR Core and platform.
+# ------------------------------
+#
+# For more info:
+# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification
+
+name=Watterott AVR Boards
+version=1.0.0
+
+# AVR compile variables
+# --------------------- 
+
+compiler.warning_flags=-w
+compiler.warning_flags.none=-w
+compiler.warning_flags.default=
+compiler.warning_flags.more=-Wall
+compiler.warning_flags.all=-Wall -Wextra
+
+# Default "compiler.path" is correct, change only if you want to overidde the initial value
+#compiler.path={runtime.ide.path}/hardware/tools/avr/bin/
+compiler.path={runtime.tools.avr-gcc.path}/bin/
+compiler.c.cmd=avr-gcc
+compiler.c.flags=-c -g -Os {compiler.warning_flags} -ffunction-sections -fdata-sections -MMD
+# -w flag added to avoid printing a wrong warning http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59396
+# This is fixed in gcc 4.8.3 and will be removed as soon as we update the toolchain
+compiler.c.elf.flags=-Os {compiler.warning_flags} -Wl,--gc-sections
+compiler.c.elf.cmd=avr-gcc
+compiler.S.flags=-c -g -x assembler-with-cpp
+compiler.cpp.cmd=avr-g++
+compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD
+compiler.ar.cmd=avr-ar
+compiler.ar.flags=rcs
+compiler.objcopy.cmd=avr-objcopy
+compiler.objcopy.eep.flags=-O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0
+compiler.elf2hex.flags=-O ihex -R .eeprom
+compiler.elf2hex.cmd=avr-objcopy
+compiler.ldflags=
+compiler.size.cmd=avr-size
+
+# This can be overriden in boards.txt
+build.extra_flags=
+
+# These can be overridden in platform.local.txt
+compiler.c.extra_flags=
+compiler.c.elf.extra_flags=
+compiler.S.extra_flags=
+compiler.cpp.extra_flags=
+compiler.ar.extra_flags=
+compiler.objcopy.eep.extra_flags=
+compiler.elf2hex.extra_flags=
+
+# AVR compile patterns
+# --------------------
+
+## Compile c files
+recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
+
+## Compile c++ files
+recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
+
+## Compile S files
+recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.S.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
+
+## Create archives
+recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{build.path}/{archive_file}" "{object_file}"
+
+## Combine gc-sections, archives, and objects
+recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mmcu={build.mcu} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" {object_files} "{build.path}/{archive_file}" "-L{build.path}" -lm
+
+## Create output files (.eep and .hex)
+recipe.objcopy.eep.pattern="{compiler.path}{compiler.objcopy.cmd}" {compiler.objcopy.eep.flags} {compiler.objcopy.eep.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.eep"
+recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex"
+
+## Save hex
+recipe.output.tmp_file={build.project_name}.hex
+recipe.output.save_file={build.project_name}.{build.variant}.hex
+
+## Compute size
+recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf"
+recipe.size.regex=^(?:\.text|\.data|\.bootloader)\s+([0-9]+).*
+recipe.size.regex.data=^(?:\.data|\.bss|\.noinit)\s+([0-9]+).*
+recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).*
+
+## Preprocessor
+preproc.includes.flags=-w -x c++ -M -MG -MP
+recipe.preproc.includes="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} {preproc.includes.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}"
+preproc.macros.flags=-w -x c++ -E -CC
+recipe.preproc.macros="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} {preproc.macros.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}"
+
+
+# AVR Uploader/Programmers tools
+# ------------------------------
+
+## AVRdude
+#tools.avrdude.path={runtime.ide.path}/hardware/tools/avr/
+tools.avrdude.path={runtime.tools.avrdude.path}
+tools.avrdude.cmd.path={path}/bin/avrdude
+#tools.avrdude.config.path={path}/etc/avrdude.conf
+tools.avrdude.config.path={runtime.platform.path}/tools/avrdude.conf
+
+tools.avrdude.upload.params.verbose=-v
+tools.avrdude.upload.params.quiet=-q -q
+tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} -p{build.mcu} -c{upload.protocol} -P{serial.port} -b{upload.speed} -D "-Uflash:w:{build.path}/{build.project_name}.hex:i"
+
+tools.avrdude.program.params.verbose=-v
+tools.avrdude.program.params.quiet=-q -q
+tools.avrdude.program.pattern="{cmd.path}" "-C{config.path}" {program.verbose} -p{build.mcu} -c{protocol} -P{serial.port} {program.extra_params} "-Uflash:w:{build.path}/{build.project_name}.hex:i"
+
+tools.avrdude.erase.params.verbose=-v
+tools.avrdude.erase.params.quiet=-q -q
+tools.avrdude.erase.pattern="{cmd.path}" "-C{config.path}" {erase.verbose} -p{build.mcu} -c{protocol} -P{serial.port} {program.extra_params} -e -Ulock:w:{bootloader.unlock_bits}:m -Uefuse:w:{bootloader.extended_fuses}:m -Uhfuse:w:{bootloader.high_fuses}:m -Ulfuse:w:{bootloader.low_fuses}:m
+
+tools.avrdude.bootloader.params.verbose=-v
+tools.avrdude.bootloader.params.quiet=-q -q
+tools.avrdude.bootloader.pattern="{cmd.path}" "-C{config.path}" {bootloader.verbose} -p{build.mcu} -c{protocol} -P{serial.port} {program.extra_params} "-Uflash:w:{runtime.platform.path}/bootloaders/{bootloader.file}:i" -Ulock:w:{bootloader.lock_bits}:m
+
+## Micronucleus
+tools.micronucleus.path={runtime.tools.micronucleus.path}
+tools.micronucleus.cmd=micronucleus
+tools.micronucleus.cmd.windows=micronucleus.exe
+
+tools.micronucleus.upload.params.verbose=
+tools.micronucleus.upload.params.quiet=
+tools.micronucleus.upload.pattern="{path}/{cmd}" --run --timeout 60 "{build.path}/{build.project_name}.hex"
+
+tools.micronucleus.program.params.verbose=
+tools.micronucleus.program.params.quiet=
+tools.micronucleus.program.pattern="{path}/{cmd}" --run --timeout 60 "{build.path}/{build.project_name}.hex"
+
+tools.micronucleus.erase.params.verbose=
+tools.micronucleus.erase.params.quiet=
+tools.micronucleus.erase.pattern=
+#tools.micronucleus.erase.pattern="{path}/{cmd}" --erase-only --timeout 60
+
+tools.micronucleus.bootloader.params.verbose=
+tools.micronucleus.bootloader.params.quiet=
+tools.micronucleus.bootloader.pattern="{path}/{cmd}" --run --timeout 60 "{runtime.platform.path}/bootloaders/{bootloader.file}"
+
+
+# USB Default Flags
+# Default blank usb manufacturer will be filled it at compile time
+# - from numeric vendor ID, set to Unknown otherwise
+build.usb_manufacturer="Unknown"
+build.usb_flags=-DUSB_VID={build.vid} -DUSB_PID={build.pid} '-DUSB_MANUFACTURER={build.usb_manufacturer}' '-DUSB_PRODUCT={build.usb_product}'
+
diff --git a/arduino/cores/packagemanager/testdata/custom_hardware/watterott/avr/programmers.txt b/arduino/cores/packagemanager/testdata/custom_hardware/watterott/avr/programmers.txt
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/commands/board/details.go b/commands/board/details.go
index 09cd3ec88c1..6404e5b9653 100644
--- a/commands/board/details.go
+++ b/commands/board/details.go
@@ -55,6 +55,10 @@ func Details(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetai
 			Properties: p.AsMap(),
 		})
 	}
+	for _, k := range boardProperties.Keys() {
+		v := boardProperties.Get(k)
+		details.BuildProperties = append(details.BuildProperties, k+"="+v)
+	}
 
 	details.DebuggingSupported = boardProperties.ContainsKey("debug.executable") ||
 		boardPlatform.Properties.ContainsKey("debug.executable") ||
diff --git a/commands/compile/compile.go b/commands/compile/compile.go
index 152303ee0c1..72c476daf6a 100644
--- a/commands/compile/compile.go
+++ b/commands/compile/compile.go
@@ -25,7 +25,6 @@ import (
 	"github.com/arduino/arduino-cli/arduino"
 	bldr "github.com/arduino/arduino-cli/arduino/builder"
 	"github.com/arduino/arduino-cli/arduino/cores"
-	"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
 	"github.com/arduino/arduino-cli/arduino/sketch"
 	"github.com/arduino/arduino-cli/buildcache"
 	"github.com/arduino/arduino-cli/commands"
@@ -36,6 +35,7 @@ import (
 	"github.com/arduino/arduino-cli/legacy/builder/types"
 	rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
 	paths "github.com/arduino/go-paths-helper"
+	"github.com/arduino/go-properties-orderedmap"
 	"github.com/sirupsen/logrus"
 )
 
@@ -90,25 +90,79 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
 	if err != nil {
 		return nil, &arduino.InvalidFQBNError{Cause: err}
 	}
-
-	targetPlatform := pme.FindPlatform(&packagemanager.PlatformReference{
-		Package:              fqbn.Package,
-		PlatformArchitecture: fqbn.PlatformArch,
-	})
-	if targetPlatform == nil || pme.GetInstalledPlatformRelease(targetPlatform) == nil {
-		return nil, &arduino.PlatformNotFoundError{
-			Platform: fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch),
-			Cause:    fmt.Errorf(tr("platform not installed")),
+	targetPackage, targetPlatform, targetBoard, buildProperties, buildPlatform, err := pme.ResolveFQBN(fqbn)
+	if err != nil {
+		if targetPlatform == nil {
+			return nil, &arduino.PlatformNotFoundError{
+				Platform: fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch),
+				Cause:    fmt.Errorf(tr("platform not installed")),
+			}
 		}
+		return nil, &arduino.InvalidFQBNError{Cause: err}
 	}
 
+	r = &rpc.CompileResponse{}
+	r.BoardPlatform = targetPlatform.ToRPCPlatformReference()
+	r.BuildPlatform = buildPlatform.ToRPCPlatformReference()
+
+	// Setup sign keys if requested
+	if req.KeysKeychain != "" {
+		buildProperties.Set("build.keys.keychain", req.GetKeysKeychain())
+	}
+	if req.SignKey != "" {
+		buildProperties.Set("build.keys.sign_key", req.GetSignKey())
+	}
+	if req.EncryptKey != "" {
+		buildProperties.Set("build.keys.encrypt_key", req.GetEncryptKey())
+	}
 	// At the current time we do not have a way of knowing if a board supports the secure boot or not,
 	// so, if the flags to override the default keys are used, we try override the corresponding platform property nonetheless.
 	// It's not possible to use the default name for the keys since there could be more tools to sign and encrypt.
 	// So it's mandatory to use all three flags to sign and encrypt the binary
-	securityKeysOverride := []string{}
-	if req.KeysKeychain != "" && req.SignKey != "" && req.EncryptKey != "" {
-		securityKeysOverride = append(securityKeysOverride, "build.keys.keychain="+req.KeysKeychain, "build.keys.sign_key="+req.GetSignKey(), "build.keys.encrypt_key="+req.EncryptKey)
+	keychainProp := buildProperties.ContainsKey("build.keys.keychain")
+	signProp := buildProperties.ContainsKey("build.keys.sign_key")
+	encryptProp := buildProperties.ContainsKey("build.keys.encrypt_key")
+	// we verify that all the properties for the secure boot keys are defined or none of them is defined.
+	if !(keychainProp == signProp && signProp == encryptProp) {
+		return nil, fmt.Errorf(tr("Firmware encryption/signing requires all the following properties to be defined: %s", "build.keys.keychain, build.keys.sign_key, build.keys.encrypt_key"))
+	}
+
+	// Generate or retrieve build path
+	var buildPath *paths.Path
+	if buildPathArg := req.GetBuildPath(); buildPathArg != "" {
+		buildPath = paths.New(req.GetBuildPath()).Canonical()
+		if in, err := buildPath.IsInsideDir(sk.FullPath); err != nil {
+			return nil, &arduino.NotFoundError{Message: tr("Cannot find build path"), Cause: err}
+		} else if in && buildPath.IsDir() {
+			if sk.AdditionalFiles, err = removeBuildFromSketchFiles(sk.AdditionalFiles, buildPath); err != nil {
+				return nil, err
+			}
+		}
+	}
+	if buildPath == nil {
+		buildPath = sk.DefaultBuildPath()
+	}
+	if err = buildPath.MkdirAll(); err != nil {
+		return nil, &arduino.PermissionDeniedError{Message: tr("Cannot create build directory"), Cause: err}
+	}
+	buildcache.New(buildPath.Parent()).GetOrCreate(buildPath.Base())
+	// cache is purged after compilation to not remove entries that might be required
+	defer maybePurgeBuildCache()
+
+	// Add build properites related to sketch data
+	buildProperties = bldr.SetupBuildProperties(buildProperties, buildPath, sk, req.GetOptimizeForDebug())
+
+	// Add user provided custom build properties
+	customBuildPropertiesArgs := append(req.GetBuildProperties(), "build.warn_data_percentage=75")
+	if customBuildProperties, err := properties.LoadFromSlice(req.GetBuildProperties()); err == nil {
+		buildProperties.Merge(customBuildProperties)
+	} else {
+		return nil, &arduino.InvalidArgumentError{Message: tr("Invalid build properties"), Cause: err}
+	}
+
+	requiredTools, err := pme.FindToolsRequiredForBuild(targetPlatform, buildPlatform)
+	if err != nil {
+		return nil, err
 	}
 
 	builderCtx := &types.Context{}
@@ -116,55 +170,34 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
 	if pme.GetProfile() != nil {
 		builderCtx.LibrariesManager = lm
 	}
+	builderCtx.TargetBoard = targetBoard
+	builderCtx.TargetPlatform = targetPlatform
+	builderCtx.TargetPackage = targetPackage
+	builderCtx.ActualPlatform = buildPlatform
+	builderCtx.RequiredTools = requiredTools
+	builderCtx.BuildProperties = buildProperties
+	builderCtx.CustomBuildProperties = customBuildPropertiesArgs
 	builderCtx.UseCachedLibrariesResolution = req.GetSkipLibrariesDiscovery()
 	builderCtx.FQBN = fqbn
 	builderCtx.Sketch = sk
+	builderCtx.BuildPath = buildPath
 	builderCtx.ProgressCB = progressCB
 
 	// FIXME: This will be redundant when arduino-builder will be part of the cli
 	builderCtx.HardwareDirs = configuration.HardwareDirectories(configuration.Settings)
 	builderCtx.BuiltInToolsDirs = configuration.BuiltinToolsDirectories(configuration.Settings)
-
-	// FIXME: This will be redundant when arduino-builder will be part of the cli
 	builderCtx.OtherLibrariesDirs = paths.NewPathList(req.GetLibraries()...)
 	builderCtx.OtherLibrariesDirs.Add(configuration.LibrariesDir(configuration.Settings))
 	builderCtx.LibraryDirs = paths.NewPathList(req.Library...)
-	if req.GetBuildPath() == "" {
-		builderCtx.BuildPath = sk.DefaultBuildPath()
-	} else {
-		builderCtx.BuildPath = paths.New(req.GetBuildPath()).Canonical()
-		if in, err := builderCtx.BuildPath.IsInsideDir(sk.FullPath); err != nil {
-			return nil, &arduino.NotFoundError{Message: tr("Cannot find build path"), Cause: err}
-		} else if in && builderCtx.BuildPath.IsDir() {
-			if sk.AdditionalFiles, err = removeBuildFromSketchFiles(sk.AdditionalFiles, builderCtx.BuildPath); err != nil {
-				return nil, err
-			}
-		}
-	}
-	if err = builderCtx.BuildPath.MkdirAll(); err != nil {
-		return nil, &arduino.PermissionDeniedError{Message: tr("Cannot create build directory"), Cause: err}
-	}
-
-	buildcache.New(builderCtx.BuildPath.Parent()).GetOrCreate(builderCtx.BuildPath.Base())
-	// cache is purged after compilation to not remove entries that might be required
-	defer maybePurgeBuildCache()
 
 	builderCtx.CompilationDatabase = bldr.NewCompilationDatabase(
 		builderCtx.BuildPath.Join("compile_commands.json"),
 	)
 
 	builderCtx.Verbose = req.GetVerbose()
-
-	// Optimize for debug
-	builderCtx.OptimizeForDebug = req.GetOptimizeForDebug()
-
 	builderCtx.Jobs = int(req.GetJobs())
-
 	builderCtx.WarningsLevel = req.GetWarnings()
 
-	builderCtx.CustomBuildProperties = append(req.GetBuildProperties(), "build.warn_data_percentage=75")
-	builderCtx.CustomBuildProperties = append(req.GetBuildProperties(), securityKeysOverride...)
-
 	if req.GetBuildCachePath() == "" {
 		builderCtx.CoreBuildCachePath = paths.TempDir().Join("arduino", "cores")
 	} else {
@@ -186,17 +219,10 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
 	builderCtx.OnlyUpdateCompilationDatabase = req.GetCreateCompilationDatabaseOnly()
 	builderCtx.SourceOverride = req.GetSourceOverride()
 
-	r = &rpc.CompileResponse{}
 	defer func() {
 		if p := builderCtx.BuildPath; p != nil {
 			r.BuildPath = p.String()
 		}
-		if pl := builderCtx.TargetPlatform; pl != nil {
-			r.BoardPlatform = pl.ToRPCPlatformReference()
-		}
-		if pl := builderCtx.ActualPlatform; pl != nil {
-			r.BuildPlatform = pl.ToRPCPlatformReference()
-		}
 	}()
 
 	defer func() {
@@ -243,6 +269,30 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
 	}()
 
 	// if it's a regular build, go on...
+
+	if req.GetVerbose() {
+		core := buildProperties.Get("build.core")
+		if core == "" {
+			core = "arduino"
+		}
+		// select the core name in case of "package:core" format
+		normalizedFQBN, err := pme.NormalizeFQBN(fqbn)
+		if err != nil {
+			outStream.Write([]byte(fmt.Sprintf("Could not normalize FQBN: %s\n", err)))
+			normalizedFQBN = fqbn
+		}
+		outStream.Write([]byte(fmt.Sprintf("FQBN: %s\n", normalizedFQBN)))
+		core = core[strings.Index(core, ":")+1:]
+		outStream.Write([]byte(tr("Using board '%[1]s' from platform in folder: %[2]s", targetBoard.BoardID, targetPlatform.InstallDir) + "\n"))
+		outStream.Write([]byte(tr("Using core '%[1]s' from platform in folder: %[2]s", core, buildPlatform.InstallDir) + "\n"))
+		outStream.Write([]byte("\n"))
+	}
+	if !targetBoard.Properties.ContainsKey("build.board") {
+		outStream.Write([]byte(
+			tr("Warning: Board %[1]s doesn't define a %[2]s preference. Auto-set to: %[3]s",
+				targetBoard.String(), "'build.board'", buildProperties.Get("build.board")) + "\n"))
+	}
+
 	if err := builder.RunBuilder(builderCtx); err != nil {
 		return r, &arduino.CompileFailedError{Message: err.Error()}
 	}
diff --git a/internal/cli/arguments/show_properties.go b/internal/cli/arguments/show_properties.go
new file mode 100644
index 00000000000..7a6d3a2ee71
--- /dev/null
+++ b/internal/cli/arguments/show_properties.go
@@ -0,0 +1,62 @@
+// This file is part of arduino-cli.
+//
+// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
+//
+// This software is released under the GNU General Public License version 3,
+// which covers the main part of arduino-cli.
+// The terms of this license can be found at:
+// https://www.gnu.org/licenses/gpl-3.0.en.html
+//
+// You can be released from the requirements of the above licenses by purchasing
+// a commercial license. Buying such a license is mandatory if you want to
+// modify or otherwise use the software for commercial activities involving the
+// Arduino software without disclosing the source code of your own applications.
+// To purchase a commercial license, send an email to license@arduino.cc.
+
+package arguments
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+)
+
+// ShowProperties represents the --show-properties flag.
+type ShowProperties struct {
+	arg string
+}
+
+// ShowPropertiesMode represents the possible values of the --show-properties flag.
+type ShowPropertiesMode int
+
+const (
+	// ShowPropertiesDisabled means that the --show-properties flag has not been used
+	ShowPropertiesDisabled ShowPropertiesMode = iota
+	// ShowPropertiesUnexpanded means that the --show-properties flag has been used without a value or with the value "unexpanded"
+	ShowPropertiesUnexpanded
+	// ShowPropertiesExpanded means that the --show-properties flag has been used with the value "expanded"
+	ShowPropertiesExpanded
+)
+
+// Get returns the corresponding ShowProperties value.
+func (p *ShowProperties) Get() (ShowPropertiesMode, error) {
+	switch p.arg {
+	case "disabled":
+		return ShowPropertiesDisabled, nil
+	case "unexpanded":
+		return ShowPropertiesUnexpanded, nil
+	case "expanded":
+		return ShowPropertiesExpanded, nil
+	default:
+		return ShowPropertiesDisabled, fmt.Errorf(tr("invalid option '%s'.", p.arg))
+	}
+}
+
+// AddToCommand adds the --show-properties flag to the specified command.
+func (p *ShowProperties) AddToCommand(command *cobra.Command) {
+	command.Flags().StringVar(&p.arg,
+		"show-properties", "disabled",
+		tr(`Show build properties. The properties are returned exactly as they are defined. Use "--show-properties=expanded" to replace placeholders with context values.`),
+	)
+	command.Flags().Lookup("show-properties").NoOptDefVal = "unexpanded" // default if the flag is present with no value
+}
diff --git a/internal/cli/board/attach.go b/internal/cli/board/attach.go
index d701ad15e8a..09d4e705eac 100644
--- a/internal/cli/board/attach.go
+++ b/internal/cli/board/attach.go
@@ -26,6 +26,7 @@ import (
 
 func initAttachCommand() *cobra.Command {
 	var port arguments.Port
+	var fqbn arguments.Fqbn
 	attachCommand := &cobra.Command{
 		Use:   fmt.Sprintf("attach [-p <%s>] [-b <%s>] [%s]", tr("port"), tr("FQBN"), tr("sketchPath")),
 		Short: tr("Attaches a sketch to a board."),
diff --git a/internal/cli/board/details.go b/internal/cli/board/details.go
index b8b710b1878..7135a3faf0a 100644
--- a/internal/cli/board/details.go
+++ b/internal/cli/board/details.go
@@ -22,6 +22,7 @@ import (
 
 	"github.com/arduino/arduino-cli/commands/board"
 	"github.com/arduino/arduino-cli/internal/cli/arguments"
+	"github.com/arduino/arduino-cli/internal/cli/compile"
 	"github.com/arduino/arduino-cli/internal/cli/feedback"
 	"github.com/arduino/arduino-cli/internal/cli/instance"
 	rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
@@ -31,51 +32,66 @@ import (
 	"github.com/spf13/cobra"
 )
 
-var (
-	showFullDetails bool
-	listProgrammers bool
-	fqbn            arguments.Fqbn
-)
-
 func initDetailsCommand() *cobra.Command {
+	var showFullDetails bool
+	var listProgrammers bool
+	var fqbn arguments.Fqbn
+	var showProperties arguments.ShowProperties
 	var detailsCommand = &cobra.Command{
 		Use:     fmt.Sprintf("details -b <%s>", tr("FQBN")),
 		Short:   tr("Print details about a board."),
 		Long:    tr("Show information about a board, in particular if the board has options to be specified in the FQBN."),
 		Example: "  " + os.Args[0] + " board details -b arduino:avr:nano",
 		Args:    cobra.NoArgs,
-		Run:     runDetailsCommand,
+		Run: func(cmd *cobra.Command, args []string) {
+			runDetailsCommand(fqbn.String(), showFullDetails, listProgrammers, showProperties)
+		},
 	}
 
 	fqbn.AddToCommand(detailsCommand)
 	detailsCommand.Flags().BoolVarP(&showFullDetails, "full", "f", false, tr("Show full board details"))
 	detailsCommand.Flags().BoolVarP(&listProgrammers, "list-programmers", "", false, tr("Show list of available programmers"))
 	detailsCommand.MarkFlagRequired("fqbn")
-
+	showProperties.AddToCommand(detailsCommand)
 	return detailsCommand
 }
 
-func runDetailsCommand(cmd *cobra.Command, args []string) {
+func runDetailsCommand(fqbn string, showFullDetails, listProgrammers bool, showProperties arguments.ShowProperties) {
 	inst := instance.CreateAndInit()
 
 	logrus.Info("Executing `arduino-cli board details`")
 
+	showPropertiesMode, err := showProperties.Get()
+	if err != nil {
+		feedback.Fatal(err.Error(), feedback.ErrBadArgument)
+	}
 	res, err := board.Details(context.Background(), &rpc.BoardDetailsRequest{
 		Instance: inst,
-		Fqbn:     fqbn.String(),
+		Fqbn:     fqbn,
 	})
-
 	if err != nil {
 		feedback.Fatal(tr("Error getting board details: %v", err), feedback.ErrGeneric)
 	}
 
-	feedback.PrintResult(detailsResult{details: res})
+	if showPropertiesMode == arguments.ShowPropertiesExpanded {
+		res.BuildProperties, _ = compile.ExpandBuildProperties(res.GetBuildProperties())
+	}
+
+	feedback.PrintResult(detailsResult{
+		details:         res,
+		listProgrammers: listProgrammers,
+		showFullDetails: showFullDetails,
+		showProperties:  showPropertiesMode != arguments.ShowPropertiesDisabled,
+	})
 }
 
 // output from this command requires special formatting, let's create a dedicated
 // feedback.Result implementation
 type detailsResult struct {
-	details *rpc.BoardDetailsResponse
+	details         *rpc.BoardDetailsResponse
+	listProgrammers bool
+	showFullDetails bool
+	showProperties  bool
 }
 
 func (dr detailsResult) Data() interface{} {
@@ -85,7 +101,15 @@ func (dr detailsResult) Data() interface{} {
 func (dr detailsResult) String() string {
 	details := dr.details
 
-	if listProgrammers {
+	if dr.showProperties {
+		res := ""
+		for _, prop := range details.GetBuildProperties() {
+			res += fmt.Sprintln(prop)
+		}
+		return res
+	}
+
+	if dr.listProgrammers {
 		t := table.New()
 		t.AddRow(tr("Id"), tr("Programmer name"))
 		for _, programmer := range details.Programmers {
@@ -160,7 +184,7 @@ func (dr detailsResult) String() string {
 	tab.SetColumnWidthMode(1, table.Average)
 	for _, tool := range details.ToolsDependencies {
 		tab.AddRow(tr("Required tool:"), tool.Packager+":"+tool.Name, tool.Version)
-		if showFullDetails {
+		if dr.showFullDetails {
 			for _, sys := range tool.Systems {
 				tab.AddRow("", tr("OS:"), sys.Host)
 				tab.AddRow("", tr("File:"), sys.ArchiveFilename)
diff --git a/internal/cli/board/list.go b/internal/cli/board/list.go
index 4f810f76f5d..2021f5bcbb5 100644
--- a/internal/cli/board/list.go
+++ b/internal/cli/board/list.go
@@ -33,43 +33,42 @@ import (
 	"github.com/spf13/cobra"
 )
 
-var (
-	timeoutArg arguments.DiscoveryTimeout
-	watch      bool
-)
-
 func initListCommand() *cobra.Command {
+	var timeoutArg arguments.DiscoveryTimeout
+	var watch bool
+	var fqbn arguments.Fqbn
 	listCommand := &cobra.Command{
 		Use:     "list",
 		Short:   tr("List connected boards."),
 		Long:    tr("Detects and displays a list of boards connected to the current computer."),
 		Example: "  " + os.Args[0] + " board list --discovery-timeout 10s",
 		Args:    cobra.NoArgs,
-		Run:     runListCommand,
+		Run: func(cmd *cobra.Command, args []string) {
+			runListCommand(watch, timeoutArg.Get().Milliseconds(), fqbn.String())
+		},
 	}
 
 	timeoutArg.AddToCommand(listCommand)
 	fqbn.AddToCommand(listCommand)
 	listCommand.Flags().BoolVarP(&watch, "watch", "w", false, tr("Command keeps running and prints list of connected boards whenever there is a change."))
-
 	return listCommand
 }
 
 // runListCommand detects and lists the connected arduino boards
-func runListCommand(cmd *cobra.Command, args []string) {
+func runListCommand(watch bool, timeout int64, fqbn string) {
 	inst := instance.CreateAndInit()
 
 	logrus.Info("Executing `arduino-cli board list`")
 
 	if watch {
-		watchList(cmd, inst)
+		watchList(inst)
 		return
 	}
 
 	ports, discoveryErrors, err := board.List(&rpc.BoardListRequest{
 		Instance: inst,
-		Timeout:  timeoutArg.Get().Milliseconds(),
-		Fqbn:     fqbn.String(),
+		Timeout:  timeout,
+		Fqbn:     fqbn,
 	})
 	var invalidFQBNErr *arduino.InvalidFQBNError
 	if errors.As(err, &invalidFQBNErr) {
@@ -84,7 +83,7 @@ func runListCommand(cmd *cobra.Command, args []string) {
 	feedback.PrintResult(result{ports})
 }
 
-func watchList(cmd *cobra.Command, inst *rpc.Instance) {
+func watchList(inst *rpc.Instance) {
 	eventsChan, closeCB, err := board.Watch(&rpc.BoardListWatchRequest{Instance: inst})
 	if err != nil {
 		feedback.Fatal(tr("Error detecting boards: %v", err), feedback.ErrNetwork)
diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go
index 3eb8708ed16..6a1ee024bf5 100644
--- a/internal/cli/compile/compile.go
+++ b/internal/cli/compile/compile.go
@@ -44,50 +44,30 @@ import (
 	"github.com/spf13/cobra"
 )
 
-type showPropertiesMode int
-
-const (
-	showPropertiesModeDisabled showPropertiesMode = iota
-	showPropertiesModeUnexpanded
-	showPropertiesModeExpanded
-)
-
-func parseShowPropertiesMode(showProperties string) (showPropertiesMode, error) {
-	val, ok := map[string]showPropertiesMode{
-		"disabled":   showPropertiesModeDisabled,
-		"unexpanded": showPropertiesModeUnexpanded,
-		"expanded":   showPropertiesModeExpanded,
-	}[showProperties]
-	if !ok {
-		return showPropertiesModeDisabled, fmt.Errorf(tr("invalid option '%s'.", showProperties))
-	}
-	return val, nil
-}
-
 var (
-	fqbnArg                 arguments.Fqbn       // Fully Qualified Board Name, e.g.: arduino:avr:uno.
-	profileArg              arguments.Profile    // Profile to use
-	showProperties          string               // Show all build preferences used instead of compiling.
-	preprocess              bool                 // Print preprocessed code to stdout.
-	buildCachePath          string               // Builds of 'core.a' are saved into this path to be cached and reused.
-	buildPath               string               // Path where to save compiled files.
-	buildProperties         []string             // List of custom build properties separated by commas. Or can be used multiple times for multiple properties.
-	keysKeychain            string               // The path of the dir where to search for the custom keys to sign and encrypt a binary. Used only by the platforms that supports it
-	signKey                 string               // The name of the custom signing key to use to sign a binary during the compile process. Used only by the platforms that supports it
-	encryptKey              string               // The name of the custom encryption key to use to encrypt a binary during the compile process. Used only by the platforms that supports it
-	warnings                string               // Used to tell gcc which warning level to use.
-	verbose                 bool                 // Turns on verbose mode.
-	quiet                   bool                 // Suppresses almost every output.
-	uploadAfterCompile      bool                 // Upload the binary after the compilation.
-	portArgs                arguments.Port       // Upload port, e.g.: COM10 or /dev/ttyACM0.
-	verify                  bool                 // Upload, verify uploaded binary after the upload.
-	exportDir               string               // The compiled binary is written to this file
-	optimizeForDebug        bool                 // Optimize compile output for debug, not for release
-	programmer              arguments.Programmer // Use the specified programmer to upload
-	clean                   bool                 // Cleanup the build folder and do not use any cached build
-	compilationDatabaseOnly bool                 // Only create compilation database without actually compiling
-	sourceOverrides         string               // Path to a .json file that contains a set of replacements of the sketch source code.
-	dumpProfile             bool                 // Create and print a profile configuration from the build
+	fqbnArg                 arguments.Fqbn           // Fully Qualified Board Name, e.g.: arduino:avr:uno.
+	profileArg              arguments.Profile        // Profile to use
+	showPropertiesArg       arguments.ShowProperties // Show all build preferences used instead of compiling.
+	preprocess              bool                     // Print preprocessed code to stdout.
+	buildCachePath          string                   // Builds of 'core.a' are saved into this path to be cached and reused.
+	buildPath               string                   // Path where to save compiled files.
+	buildProperties         []string                 // List of custom build properties separated by commas. Or can be used multiple times for multiple properties.
+	keysKeychain            string                   // The path of the dir where to search for the custom keys to sign and encrypt a binary. Used only by the platforms that supports it
+	signKey                 string                   // The name of the custom signing key to use to sign a binary during the compile process. Used only by the platforms that supports it
+	encryptKey              string                   // The name of the custom encryption key to use to encrypt a binary during the compile process. Used only by the platforms that supports it
+	warnings                string                   // Used to tell gcc which warning level to use.
+	verbose                 bool                     // Turns on verbose mode.
+	quiet                   bool                     // Suppresses almost every output.
+	uploadAfterCompile      bool                     // Upload the binary after the compilation.
+	portArgs                arguments.Port           // Upload port, e.g.: COM10 or /dev/ttyACM0.
+	verify                  bool                     // Upload, verify uploaded binary after the upload.
+	exportDir               string                   // The compiled binary is written to this file
+	optimizeForDebug        bool                     // Optimize compile output for debug, not for release
+	programmer              arguments.Programmer     // Use the specified programmer to upload
+	clean                   bool                     // Cleanup the build folder and do not use any cached build
+	compilationDatabaseOnly bool                     // Only create compilation database without actually compiling
+	sourceOverrides         string                   // Path to a .json file that contains a set of replacements of the sketch source code.
+	dumpProfile             bool                     // Create and print a profile configuration from the build
 	// library and libraries sound similar but they're actually different.
 	// library expects a path to the root folder of one single library.
 	// libraries expects a path to a directory containing multiple libraries, similarly to the <directories.user>/libraries path.
@@ -115,13 +95,7 @@ func NewCommand() *cobra.Command {
 	fqbnArg.AddToCommand(compileCommand)
 	profileArg.AddToCommand(compileCommand)
 	compileCommand.Flags().BoolVar(&dumpProfile, "dump-profile", false, tr("Create and print a profile configuration from the build."))
-	compileCommand.Flags().StringVar(
-		&showProperties,
-		"show-properties",
-		"disabled",
-		tr(`Show build properties instead of compiling. The properties are returned exactly as they are defined. Use "--show-properties=expanded" to replace placeholders with compilation context values.`),
-	)
-	compileCommand.Flags().Lookup("show-properties").NoOptDefVal = "unexpanded" // default if the flag is present with no value
+	showPropertiesArg.AddToCommand(compileCommand)
 	compileCommand.Flags().BoolVar(&preprocess, "preprocess", false, tr("Print preprocessed code to stdout instead of compiling."))
 	compileCommand.Flags().StringVar(&buildCachePath, "build-cache-path", "", tr("Builds of 'core.a' are saved into this path to be cached and reused."))
 	compileCommand.Flags().StringVarP(&exportDir, "output-dir", "", "", tr("Save build artifacts in this directory."))
@@ -213,14 +187,14 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
 		overrides = o.Overrides
 	}
 
-	showPropertiesM, err := parseShowPropertiesMode(showProperties)
+	showProperties, err := showPropertiesArg.Get()
 	if err != nil {
 		feedback.Fatal(tr("Error parsing --show-properties flag: %v", err), feedback.ErrGeneric)
 	}
 
 	var stdOut, stdErr io.Writer
 	var stdIORes func() *feedback.OutputStreamsResult
-	if showPropertiesM != showPropertiesModeDisabled {
+	if showProperties != arguments.ShowPropertiesDisabled {
 		stdOut, stdErr, stdIORes = feedback.NewBufferedStreams()
 	} else {
 		stdOut, stdErr, stdIORes = feedback.OutputStreams()
@@ -238,7 +212,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
 		Instance:                      inst,
 		Fqbn:                          fqbn,
 		SketchPath:                    sketchPath.String(),
-		ShowProperties:                showPropertiesM != showPropertiesModeDisabled,
+		ShowProperties:                showProperties != arguments.ShowPropertiesDisabled,
 		Preprocess:                    preprocess,
 		BuildCachePath:                buildCachePath,
 		BuildPath:                     buildPath,
@@ -355,7 +329,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
 		BuilderResult:      compileRes,
 		ProfileOut:         profileOut,
 		Success:            compileError == nil,
-		showPropertiesMode: showPropertiesM,
+		showPropertiesMode: showProperties,
 	}
 
 	if compileError != nil {
@@ -390,22 +364,25 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
 		}
 		feedback.FatalResult(res, feedback.ErrGeneric)
 	}
-	if showPropertiesM == showPropertiesModeExpanded {
-		expandPropertiesInResult(res)
+	if showProperties == arguments.ShowPropertiesExpanded {
+		res.BuilderResult.BuildProperties, _ = ExpandBuildProperties(res.BuilderResult.GetBuildProperties())
+		// ignore error, it should never fail
 	}
 	feedback.PrintResult(res)
 }
 
-func expandPropertiesInResult(res *compileResult) {
-	expanded, err := properties.LoadFromSlice(res.BuilderResult.GetBuildProperties())
+// ExpandBuildProperties expands the build properties placeholders in the slice of properties.
+func ExpandBuildProperties(props []string) ([]string, error) {
+	expanded, err := properties.LoadFromSlice(props)
 	if err != nil {
-		res.Error = tr(err.Error())
+		return nil, err
 	}
-	expandedSlice := make([]string, expanded.Size())
-	for i, k := range expanded.Keys() {
-		expandedSlice[i] = strings.Join([]string{k, expanded.ExpandPropsInString(expanded.Get(k))}, "=")
+	expandedProps := []string{}
+	for _, k := range expanded.Keys() {
+		v := expanded.Get(k)
+		expandedProps = append(expandedProps, k+"="+expanded.ExpandPropsInString(v))
 	}
-	res.BuilderResult.BuildProperties = expandedSlice
+	return expandedProps, nil
 }
 
 type compileResult struct {
@@ -416,7 +393,7 @@ type compileResult struct {
 	ProfileOut    string               `json:"profile_out,omitempty"`
 	Error         string               `json:"error,omitempty"`
 
-	showPropertiesMode showPropertiesMode
+	showPropertiesMode arguments.ShowPropertiesMode
 }
 
 func (r *compileResult) Data() interface{} {
@@ -424,7 +401,7 @@ func (r *compileResult) Data() interface{} {
 }
 
 func (r *compileResult) String() string {
-	if r.showPropertiesMode != showPropertiesModeDisabled {
+	if r.showPropertiesMode != arguments.ShowPropertiesDisabled {
 		return strings.Join(r.BuilderResult.GetBuildProperties(), fmt.Sprintln())
 	}
 
diff --git a/internal/integrationtest/compile_1/compile_test.go b/internal/integrationtest/compile_1/compile_test.go
index 174d8f1eaf9..3d0115923a1 100644
--- a/internal/integrationtest/compile_1/compile_test.go
+++ b/internal/integrationtest/compile_1/compile_test.go
@@ -734,13 +734,11 @@ func compileUsingBoardsLocalTxt(t *testing.T, env *integrationtest.Environment,
 	// Verifies compilation fails because board doesn't exist
 	_, stderr, err := cli.Run("compile", "--clean", "-b", fqbn, sketchPath.String())
 	require.Error(t, err)
-	require.Contains(t, string(stderr), "Error during build: Error resolving FQBN: board arduino:avr:nessuno not found")
+	require.Contains(t, string(stderr), "Error during build: Invalid FQBN: board arduino:avr:nessuno not found")
 
 	// Use custom boards.local.txt with made arduino:avr:nessuno board
 	boardsLocalTxt := cli.DataDir().Join("packages", "arduino", "hardware", "avr", "1.8.5", "boards.local.txt")
-	wd, err := paths.Getwd()
-	require.NoError(t, err)
-	err = wd.Parent().Join("testdata", "boards.local.txt").CopyTo(boardsLocalTxt)
+	err = paths.New("..", "testdata", "boards.local.txt").CopyTo(boardsLocalTxt)
 	require.NoError(t, err)
 	// Remove the file at the end of the test to avoid disrupting following tests
 	defer boardsLocalTxt.Remove()
diff --git a/internal/integrationtest/compile_2/compile_test.go b/internal/integrationtest/compile_2/compile_test.go
index f37f23eab19..691eb6ffdf0 100644
--- a/internal/integrationtest/compile_2/compile_test.go
+++ b/internal/integrationtest/compile_2/compile_test.go
@@ -163,13 +163,10 @@ func TestCompileManuallyInstalledPlatformUsingBoardsLocalTxt(t *testing.T) {
 	env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
 	defer env.CleanUp()
 
-	_, _, err := cli.Run("update")
-	require.NoError(t, err)
-
 	sketchName := "CompileSketchManuallyInstalledPlatformUsingBoardsLocalTxt"
 	sketchPath := cli.SketchbookDir().Join(sketchName)
 	fqbn := "arduino-beta-development:avr:nessuno"
-	_, _, err = cli.Run("sketch", "new", sketchPath.String())
+	_, _, err := cli.Run("sketch", "new", sketchPath.String())
 	require.NoError(t, err)
 
 	// Manually installs a core in sketchbooks hardware folder
@@ -188,7 +185,7 @@ func TestCompileManuallyInstalledPlatformUsingBoardsLocalTxt(t *testing.T) {
 	// Verifies compilation fails because board doesn't exist
 	_, stderr, err := cli.Run("compile", "--clean", "-b", fqbn, sketchPath.String())
 	require.Error(t, err)
-	require.Contains(t, string(stderr), "Error during build: Error resolving FQBN: board arduino-beta-development:avr:nessuno not found")
+	require.Contains(t, string(stderr), "Error during build: Invalid FQBN: board arduino-beta-development:avr:nessuno not found")
 
 	// Use custom boards.local.txt with made arduino:avr:nessuno board
 	boardsLocalTxt := repoDir.Join("boards.local.txt")
diff --git a/legacy/builder/container_setup.go b/legacy/builder/container_setup.go
index a89434db90a..dd52af49b8f 100644
--- a/legacy/builder/container_setup.go
+++ b/legacy/builder/container_setup.go
@@ -23,36 +23,14 @@ import (
 type ContainerSetupHardwareToolsLibsSketchAndProps struct{}
 
 func (s *ContainerSetupHardwareToolsLibsSketchAndProps) Run(ctx *types.Context) error {
-	// total number of steps in this container: 14
-	ctx.Progress.AddSubSteps(14)
+	// total number of steps in this container: 4
+	ctx.Progress.AddSubSteps(4)
 	defer ctx.Progress.RemoveSubSteps()
-
 	commands := []types.Command{
 		&AddAdditionalEntriesToContext{},
 		&FailIfBuildPathEqualsSketchPath{},
-		&HardwareLoader{},
-		&TargetBoardResolver{},
 		&LibrariesLoader{},
 	}
-
-	for _, command := range commands {
-		PrintRingNameIfDebug(ctx, command)
-		err := command.Run(ctx)
-		if err != nil {
-			return errors.WithStack(err)
-		}
-		ctx.Progress.CompleteStep()
-		ctx.PushProgress()
-	}
-
-	ctx.Progress.CompleteStep()
-	ctx.PushProgress()
-
-	commands = []types.Command{
-		&SetupBuildProperties{},
-		&SetCustomBuildProperties{},
-	}
-
 	for _, command := range commands {
 		PrintRingNameIfDebug(ctx, command)
 		err := command.Run(ctx)
@@ -62,6 +40,5 @@ func (s *ContainerSetupHardwareToolsLibsSketchAndProps) Run(ctx *types.Context)
 		ctx.Progress.CompleteStep()
 		ctx.PushProgress()
 	}
-
 	return nil
 }
diff --git a/legacy/builder/hardware_loader.go b/legacy/builder/hardware_loader.go
deleted file mode 100644
index 1ea2fbfb6b8..00000000000
--- a/legacy/builder/hardware_loader.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// This file is part of arduino-cli.
-//
-// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
-//
-// This software is released under the GNU General Public License version 3,
-// which covers the main part of arduino-cli.
-// The terms of this license can be found at:
-// https://www.gnu.org/licenses/gpl-3.0.en.html
-//
-// You can be released from the requirements of the above licenses by purchasing
-// a commercial license. Buying such a license is mandatory if you want to
-// modify or otherwise use the software for commercial activities involving the
-// Arduino software without disclosing the source code of your own applications.
-// To purchase a commercial license, send an email to license@arduino.cc.
-
-package builder
-
-import (
-	"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
-	"github.com/arduino/arduino-cli/legacy/builder/types"
-)
-
-type HardwareLoader struct{}
-
-func (s *HardwareLoader) Run(ctx *types.Context) error {
-	if ctx.PackageManager == nil {
-		// This should happen only on legacy arduino-builder.
-		// Hopefully this piece will be removed once the legacy package will be cleanedup.
-		pmb := packagemanager.NewBuilder(nil, nil, nil, nil, "arduino-builder")
-		errs := pmb.LoadHardwareFromDirectories(ctx.HardwareDirs)
-		if ctx.Verbose {
-			// With the refactoring of the initialization step of the CLI we changed how
-			// errors are returned when loading platforms and libraries, that meant returning a list of
-			// errors instead of a single one to enhance the experience for the user.
-			// I have no intention right now to start a refactoring of the legacy package too, so
-			// here's this shitty solution for now.
-			// When we're gonna refactor the legacy package this will be gone.
-			for _, err := range errs {
-				ctx.Info(tr("Error loading hardware platform: %[1]s", err.Error()))
-			}
-		}
-
-		if !ctx.CanUseCachedTools {
-			if ctx.BuiltInToolsDirs != nil {
-				pmb.LoadToolsFromBundleDirectories(ctx.BuiltInToolsDirs)
-			}
-
-			ctx.CanUseCachedTools = true
-		}
-
-		pm := pmb.Build()
-		pme, _ /* never release... */ := pm.NewExplorer()
-		ctx.PackageManager = pme
-	}
-
-	ctx.AllTools = ctx.PackageManager.GetAllInstalledToolsReleases()
-	ctx.Hardware = ctx.PackageManager.GetPackages()
-	return nil
-}
diff --git a/legacy/builder/set_custom_build_properties.go b/legacy/builder/set_custom_build_properties.go
deleted file mode 100644
index 088ee511263..00000000000
--- a/legacy/builder/set_custom_build_properties.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// This file is part of arduino-cli.
-//
-// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
-//
-// This software is released under the GNU General Public License version 3,
-// which covers the main part of arduino-cli.
-// The terms of this license can be found at:
-// https://www.gnu.org/licenses/gpl-3.0.en.html
-//
-// You can be released from the requirements of the above licenses by purchasing
-// a commercial license. Buying such a license is mandatory if you want to
-// modify or otherwise use the software for commercial activities involving the
-// Arduino software without disclosing the source code of your own applications.
-// To purchase a commercial license, send an email to license@arduino.cc.
-
-package builder
-
-import (
-	"github.com/arduino/arduino-cli/legacy/builder/types"
-	"github.com/arduino/go-properties-orderedmap"
-	"github.com/pkg/errors"
-)
-
-type SetCustomBuildProperties struct{}
-
-func (s *SetCustomBuildProperties) Run(ctx *types.Context) error {
-	buildProperties := ctx.BuildProperties
-	customBuildProperties, err := properties.LoadFromSlice(ctx.CustomBuildProperties)
-	if err != nil {
-		return errors.WithStack(err)
-	}
-
-	buildProperties.Merge(customBuildProperties)
-
-	return nil
-}
diff --git a/legacy/builder/setup_build_properties.go b/legacy/builder/setup_build_properties.go
deleted file mode 100644
index 5b191dd8dff..00000000000
--- a/legacy/builder/setup_build_properties.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// This file is part of arduino-cli.
-//
-// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
-//
-// This software is released under the GNU General Public License version 3,
-// which covers the main part of arduino-cli.
-// The terms of this license can be found at:
-// https://www.gnu.org/licenses/gpl-3.0.en.html
-//
-// You can be released from the requirements of the above licenses by purchasing
-// a commercial license. Buying such a license is mandatory if you want to
-// modify or otherwise use the software for commercial activities involving the
-// Arduino software without disclosing the source code of your own applications.
-// To purchase a commercial license, send an email to license@arduino.cc.
-
-package builder
-
-import (
-	"github.com/arduino/arduino-cli/legacy/builder/types"
-	properties "github.com/arduino/go-properties-orderedmap"
-	"github.com/pkg/errors"
-)
-
-type SetupBuildProperties struct{}
-
-func (s *SetupBuildProperties) Run(ctx *types.Context) error {
-	buildProperties := properties.NewMap()
-	buildProperties.Merge(ctx.TargetBoardBuildProperties)
-
-	if ctx.BuildPath != nil {
-		buildProperties.SetPath("build.path", ctx.BuildPath)
-	}
-	if ctx.Sketch != nil {
-		buildProperties.Set("build.project_name", ctx.Sketch.MainFile.Base())
-	}
-
-	if ctx.OptimizeForDebug {
-		if buildProperties.ContainsKey("compiler.optimization_flags.debug") {
-			buildProperties.Set("compiler.optimization_flags", buildProperties.Get("compiler.optimization_flags.debug"))
-		}
-	} else {
-		if buildProperties.ContainsKey("compiler.optimization_flags.release") {
-			buildProperties.Set("compiler.optimization_flags", buildProperties.Get("compiler.optimization_flags.release"))
-		}
-	}
-	ctx.OptimizationFlags = buildProperties.Get("compiler.optimization_flags")
-
-	buildProperties.SetPath("build.source.path", ctx.Sketch.FullPath)
-
-	keychainProp := buildProperties.ContainsKey("build.keys.keychain")
-	signProp := buildProperties.ContainsKey("build.keys.sign_key")
-	encryptProp := buildProperties.ContainsKey("build.keys.encrypt_key")
-	// we verify that all the properties for the secure boot keys are defined or none of them is defined.
-	if (keychainProp || signProp || encryptProp) && !(keychainProp && signProp && encryptProp) {
-		return errors.Errorf("%s platform does not specify correctly default sign and encryption keys", ctx.TargetPlatform.Platform)
-	}
-
-	ctx.BuildProperties = buildProperties
-
-	return nil
-}
diff --git a/legacy/builder/target_board_resolver.go b/legacy/builder/target_board_resolver.go
deleted file mode 100644
index 0d66a435b19..00000000000
--- a/legacy/builder/target_board_resolver.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// This file is part of arduino-cli.
-//
-// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
-//
-// This software is released under the GNU General Public License version 3,
-// which covers the main part of arduino-cli.
-// The terms of this license can be found at:
-// https://www.gnu.org/licenses/gpl-3.0.en.html
-//
-// You can be released from the requirements of the above licenses by purchasing
-// a commercial license. Buying such a license is mandatory if you want to
-// modify or otherwise use the software for commercial activities involving the
-// Arduino software without disclosing the source code of your own applications.
-// To purchase a commercial license, send an email to license@arduino.cc.
-
-package builder
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/arduino/arduino-cli/legacy/builder/types"
-)
-
-type TargetBoardResolver struct{}
-
-func (s *TargetBoardResolver) Run(ctx *types.Context) error {
-	targetPackage, targetPlatform, targetBoard, buildProperties, buildPlatform, err := ctx.PackageManager.ResolveFQBN(ctx.FQBN)
-	if err != nil {
-		return fmt.Errorf("%s: %w", tr("Error resolving FQBN"), err)
-	}
-
-	if ctx.Verbose {
-		core := buildProperties.Get("build.core")
-		if core == "" {
-			core = "arduino"
-		}
-		// select the core name in case of "package:core" format
-		normalizedFQBN, err := ctx.PackageManager.NormalizeFQBN(ctx.FQBN)
-		if err != nil {
-			ctx.Warn(fmt.Sprintf("Could not normalize FQBN: %s", err))
-			normalizedFQBN = ctx.FQBN
-		}
-		ctx.Info(fmt.Sprintf("FQBN: %s", normalizedFQBN))
-
-		core = core[strings.Index(core, ":")+1:]
-		ctx.Info(tr("Using board '%[1]s' from platform in folder: %[2]s", targetBoard.BoardID, targetPlatform.InstallDir))
-		ctx.Info(tr("Using core '%[1]s' from platform in folder: %[2]s", core, buildPlatform.InstallDir))
-
-		ctx.Info("")
-	}
-
-	if buildProperties.Get("build.board") == "" {
-		architecture := targetBoard.PlatformRelease.Platform.Architecture
-		defaultBuildBoard := strings.ToUpper(architecture + "_" + targetBoard.BoardID)
-		buildProperties.Set("build.board", defaultBuildBoard)
-		ctx.Info(tr("Warning: Board %[1]s doesn't define a %[2]s preference. Auto-set to: %[3]s",
-			targetBoard.String(), "'build.board'", defaultBuildBoard))
-	}
-
-	requiredTools, err := ctx.PackageManager.FindToolsRequiredForBuild(targetPlatform, buildPlatform)
-	if err != nil {
-		return err
-	}
-
-	ctx.TargetBoard = targetBoard
-	ctx.TargetBoardBuildProperties = buildProperties
-	ctx.TargetPlatform = targetPlatform
-	ctx.TargetPackage = targetPackage
-	ctx.ActualPlatform = buildPlatform
-	ctx.RequiredTools = requiredTools
-	return nil
-}
diff --git a/legacy/builder/test/add_build_board_property_if_missing_test.go b/legacy/builder/test/add_build_board_property_if_missing_test.go
deleted file mode 100644
index eeeddb5ae4a..00000000000
--- a/legacy/builder/test/add_build_board_property_if_missing_test.go
+++ /dev/null
@@ -1,95 +0,0 @@
-// This file is part of arduino-cli.
-//
-// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
-//
-// This software is released under the GNU General Public License version 3,
-// which covers the main part of arduino-cli.
-// The terms of this license can be found at:
-// https://www.gnu.org/licenses/gpl-3.0.en.html
-//
-// You can be released from the requirements of the above licenses by purchasing
-// a commercial license. Buying such a license is mandatory if you want to
-// modify or otherwise use the software for commercial activities involving the
-// Arduino software without disclosing the source code of your own applications.
-// To purchase a commercial license, send an email to license@arduino.cc.
-
-package test
-
-import (
-	"path/filepath"
-	"testing"
-
-	"github.com/arduino/arduino-cli/arduino/cores"
-	"github.com/arduino/arduino-cli/legacy/builder"
-	"github.com/arduino/arduino-cli/legacy/builder/types"
-	"github.com/arduino/go-paths-helper"
-	"github.com/stretchr/testify/require"
-)
-
-func parseFQBN(t *testing.T, fqbnIn string) *cores.FQBN {
-	fqbn, err := cores.ParseFQBN(fqbnIn)
-	require.NoError(t, err)
-	return fqbn
-}
-
-func TestAddBuildBoardPropertyIfMissing(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "user_hardware"),
-		FQBN:         parseFQBN(t, "my_avr_platform:avr:mymega"),
-		Verbose:      true,
-	}
-
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	targetPackage := ctx.TargetPackage
-	require.Equal(t, "my_avr_platform", targetPackage.Name)
-	targetPlatform := ctx.TargetPlatform
-	require.NotNil(t, targetPlatform)
-	require.NotNil(t, targetPlatform.Platform)
-	require.Equal(t, "avr", targetPlatform.Platform.Architecture)
-	targetBoard := ctx.TargetBoard
-	require.Equal(t, "mymega", targetBoard.BoardID)
-	targetBoardBuildProperties := ctx.TargetBoardBuildProperties
-	require.Equal(t, "atmega2560", targetBoardBuildProperties.Get("build.mcu"))
-	require.Equal(t, "AVR_MYMEGA", targetBoardBuildProperties.Get("build.board"))
-}
-
-func TestAddBuildBoardPropertyIfMissingNotMissing(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "user_hardware"),
-		FQBN:         parseFQBN(t, "my_avr_platform:avr:mymega:cpu=atmega1280"),
-		Verbose:      true,
-	}
-
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	targetPackage := ctx.TargetPackage
-	require.Equal(t, "my_avr_platform", targetPackage.Name)
-	targetPlatform := ctx.TargetPlatform
-	require.Equal(t, "avr", targetPlatform.Platform.Architecture)
-	targetBoard := ctx.TargetBoard
-	require.Equal(t, "mymega", targetBoard.BoardID)
-	targetBoardBuildProperties := ctx.TargetBoardBuildProperties
-	require.Equal(t, "atmega1280", targetBoardBuildProperties.Get("build.mcu"))
-	require.Equal(t, "MYMEGA1280", targetBoardBuildProperties.Get("build.board"))
-}
diff --git a/legacy/builder/test/builder_test.go b/legacy/builder/test/builder_test.go
index bcb9584a8e9..8e088eb50c1 100644
--- a/legacy/builder/test/builder_test.go
+++ b/legacy/builder/test/builder_test.go
@@ -16,11 +16,14 @@
 package test
 
 import (
+	"fmt"
 	"os/exec"
 	"path/filepath"
 	"testing"
 	"time"
 
+	bldr "github.com/arduino/arduino-cli/arduino/builder"
+	"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
 	"github.com/arduino/arduino-cli/arduino/sketch"
 	"github.com/arduino/arduino-cli/legacy/builder"
 	"github.com/arduino/arduino-cli/legacy/builder/constants"
@@ -31,33 +34,85 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func prepareBuilderTestContext(t *testing.T, sketchPath *paths.Path, fqbn string) *types.Context {
-	sk, err := sketch.New(sketchPath)
-	require.NoError(t, err)
-	return &types.Context{
-		Sketch:               sk,
-		FQBN:                 parseFQBN(t, fqbn),
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Verbose:              false,
+func cleanUpBuilderTestContext(t *testing.T, ctx *types.Context) {
+	if ctx.BuildPath != nil {
+		err := ctx.BuildPath.RemoveAll()
+		require.NoError(t, err)
 	}
 }
 
-func TestBuilderEmptySketch(t *testing.T) {
+func prepareBuilderTestContext(t *testing.T, ctx *types.Context, sketchPath *paths.Path, fqbn string) *types.Context {
 	DownloadCoresAndToolsAndLibraries(t)
 
-	ctx := prepareBuilderTestContext(t, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	if ctx == nil {
+		ctx = &types.Context{}
+	}
+	if ctx.HardwareDirs.Len() == 0 {
+		ctx.HardwareDirs = paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware")
+		ctx.BuiltInToolsDirs = paths.NewPathList("downloaded_tools")
+		ctx.BuiltInLibrariesDirs = paths.New("downloaded_libraries")
+		ctx.OtherLibrariesDirs = paths.NewPathList("libraries")
+	}
+	if ctx.BuildPath == nil {
+		buildPath, err := paths.MkTempDir("", "test_build_path")
+		NoError(t, err)
+		ctx.BuildPath = buildPath
+	}
+
+	// Create a Package Manager from the given context
+	// This should happen only on legacy arduino-builder.
+	// Hopefully this piece will be removed once the legacy package will be cleanedup.
+	pmb := packagemanager.NewBuilder(nil, nil, nil, nil, "arduino-builder")
+	for _, err := range pmb.LoadHardwareFromDirectories(ctx.HardwareDirs) {
+		// NoError(t, err)
+		fmt.Println(err)
+	}
+	if !ctx.CanUseCachedTools {
+		if ctx.BuiltInToolsDirs != nil {
+			pmb.LoadToolsFromBundleDirectories(ctx.BuiltInToolsDirs)
+		}
+		ctx.CanUseCachedTools = true
+	}
+	pm := pmb.Build()
+	pme, _ /* never release... */ := pm.NewExplorer()
+	ctx.PackageManager = pme
+
+	if sketchPath != nil {
+		sk, err := sketch.New(sketchPath)
+		require.NoError(t, err)
+		ctx.Sketch = sk
+	}
+
+	if fqbn != "" {
+		ctx.FQBN = parseFQBN(t, fqbn)
+		targetPackage, targetPlatform, targetBoard, buildProperties, buildPlatform, err := pme.ResolveFQBN(ctx.FQBN)
+		require.NoError(t, err)
+		requiredTools, err := pme.FindToolsRequiredForBuild(targetPlatform, buildPlatform)
+		require.NoError(t, err)
+
+		buildProperties = bldr.SetupBuildProperties(buildProperties, ctx.BuildPath, ctx.Sketch, false /*OptimizeForDebug*/)
+		ctx.PackageManager = pme
+		ctx.TargetBoard = targetBoard
+		ctx.BuildProperties = buildProperties
+		ctx.TargetPlatform = targetPlatform
+		ctx.TargetPackage = targetPackage
+		ctx.ActualPlatform = buildPlatform
+		ctx.RequiredTools = requiredTools
+	}
+
+	return ctx
+}
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+func TestBuilderEmptySketch(t *testing.T) {
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
 	err := command.Run(ctx)
 	NoError(t, err)
 
+	buildPath := ctx.BuildPath
 	exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
 	NoError(t, err)
 	require.True(t, exist)
@@ -76,18 +131,15 @@ func TestBuilderEmptySketch(t *testing.T) {
 }
 
 func TestBuilderBridge(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
 	err := command.Run(ctx)
 	NoError(t, err)
 
+	buildPath := ctx.BuildPath
 	exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
 	NoError(t, err)
 	require.True(t, exist)
@@ -109,18 +161,15 @@ func TestBuilderBridge(t *testing.T) {
 }
 
 func TestBuilderSketchWithConfig(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("sketch_with_config", "sketch_with_config.ino"), "arduino:avr:leonardo")
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch_with_config", "sketch_with_config.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
 	err := command.Run(ctx)
 	NoError(t, err)
 
+	buildPath := ctx.BuildPath
 	exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
 	NoError(t, err)
 	require.True(t, exist)
@@ -142,12 +191,8 @@ func TestBuilderSketchWithConfig(t *testing.T) {
 }
 
 func TestBuilderBridgeTwice(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
@@ -159,6 +204,7 @@ func TestBuilderBridgeTwice(t *testing.T) {
 	err = command.Run(ctx)
 	NoError(t, err)
 
+	buildPath := ctx.BuildPath
 	exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
 	NoError(t, err)
 	require.True(t, exist)
@@ -180,19 +226,16 @@ func TestBuilderBridgeTwice(t *testing.T) {
 }
 
 func TestBuilderBridgeSAM(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:sam:arduino_due_x_dbg")
+	ctx := prepareBuilderTestContext(t, nil, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:sam:arduino_due_x_dbg")
 	ctx.WarningsLevel = "all"
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
 	err := command.Run(ctx)
 	NoError(t, err)
 
+	buildPath := ctx.BuildPath
 	exist, err := buildPath.Join(constants.FOLDER_CORE, "syscalls_sam3.c.o").ExistCheck()
 	NoError(t, err)
 	require.True(t, exist)
@@ -225,20 +268,21 @@ func TestBuilderBridgeSAM(t *testing.T) {
 }
 
 func TestBuilderBridgeRedBearLab(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "RedBearLab:avr:blend")
-	ctx.HardwareDirs = append(ctx.HardwareDirs, paths.New("downloaded_board_manager_stuff"))
-	ctx.BuiltInToolsDirs = append(ctx.BuiltInToolsDirs, paths.New("downloaded_board_manager_stuff"))
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := &types.Context{
+		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "downloaded_board_manager_stuff"),
+		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools", "downloaded_board_manager_stuff"),
+		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
+		OtherLibrariesDirs:   paths.NewPathList("libraries"),
+	}
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "RedBearLab:avr:blend")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
 	err := command.Run(ctx)
 	NoError(t, err)
 
+	buildPath := ctx.BuildPath
 	exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
 	NoError(t, err)
 	require.True(t, exist)
@@ -260,14 +304,14 @@ func TestBuilderBridgeRedBearLab(t *testing.T) {
 }
 
 func TestBuilderSketchNoFunctions(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("sketch_no_functions", "sketch_no_functions.ino"), "RedBearLab:avr:blend")
-	ctx.HardwareDirs = append(ctx.HardwareDirs, paths.New("downloaded_board_manager_stuff"))
-	ctx.BuiltInToolsDirs = append(ctx.BuiltInToolsDirs, paths.New("downloaded_board_manager_stuff"))
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := &types.Context{
+		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "downloaded_board_manager_stuff"),
+		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools", "downloaded_board_manager_stuff"),
+		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
+		OtherLibrariesDirs:   paths.NewPathList("libraries"),
+	}
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch_no_functions", "sketch_no_functions.ino"), "RedBearLab:avr:blend")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
@@ -276,14 +320,14 @@ func TestBuilderSketchNoFunctions(t *testing.T) {
 }
 
 func TestBuilderSketchWithBackup(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("sketch_with_backup_files", "sketch_with_backup_files.ino"), "arduino:avr:uno")
-	ctx.HardwareDirs = append(ctx.HardwareDirs, paths.New("downloaded_board_manager_stuff"))
-	ctx.BuiltInToolsDirs = append(ctx.BuiltInToolsDirs, paths.New("downloaded_board_manager_stuff"))
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := &types.Context{
+		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "downloaded_board_manager_stuff"),
+		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools", "downloaded_board_manager_stuff"),
+		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
+		OtherLibrariesDirs:   paths.NewPathList("libraries"),
+	}
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch_with_backup_files", "sketch_with_backup_files.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
@@ -292,12 +336,8 @@ func TestBuilderSketchWithBackup(t *testing.T) {
 }
 
 func TestBuilderSketchWithOldLib(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("sketch_with_old_lib", "sketch_with_old_lib.ino"), "arduino:avr:uno")
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch_with_old_lib", "sketch_with_old_lib.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
@@ -306,13 +346,9 @@ func TestBuilderSketchWithOldLib(t *testing.T) {
 }
 
 func TestBuilderSketchWithSubfolders(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	logrus.SetLevel(logrus.DebugLevel)
-	ctx := prepareBuilderTestContext(t, paths.New("sketch_with_subfolders", "sketch_with_subfolders.ino"), "arduino:avr:uno")
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch_with_subfolders", "sketch_with_subfolders.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run builder
 	command := builder.Builder{}
@@ -321,13 +357,10 @@ func TestBuilderSketchWithSubfolders(t *testing.T) {
 }
 
 func TestBuilderSketchBuildPathContainsUnusedPreviouslyCompiledLibrary(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
+	buildPath := ctx.BuildPath
 	NoError(t, buildPath.Join("libraries", "SPI").MkdirAll())
 
 	// Run builder
@@ -344,14 +377,10 @@ func TestBuilderSketchBuildPathContainsUnusedPreviouslyCompiledLibrary(t *testin
 }
 
 func TestBuilderWithBuildPathInSketchDir(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
-
-	var err error
-	ctx.BuildPath, err = paths.New("sketch1", "build").Abs()
+	buildPath, err := paths.New("sketch1", "build").Abs()
 	NoError(t, err)
-	defer ctx.BuildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, &types.Context{BuildPath: buildPath}, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	// Run build
 	command := builder.Builder{}
@@ -365,12 +394,9 @@ func TestBuilderWithBuildPathInSketchDir(t *testing.T) {
 }
 
 func TestBuilderCacheCoreAFile(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := prepareBuilderTestContext(t, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	SetupBuildPath(t, ctx)
-	defer ctx.BuildPath.RemoveAll()
 	SetupBuildCachePath(t, ctx)
 	defer ctx.CoreBuildCachePath.RemoveAll()
 
@@ -381,7 +407,7 @@ func TestBuilderCacheCoreAFile(t *testing.T) {
 
 	// Pick timestamp of cached core
 	coreFolder := paths.New("downloaded_hardware", "arduino", "avr")
-	coreFileName := phases.GetCachedCoreArchiveDirName(ctx.FQBN.String(), ctx.OptimizationFlags, coreFolder)
+	coreFileName := phases.GetCachedCoreArchiveDirName(ctx.FQBN.String(), ctx.BuildProperties.Get("compiler.optimization_flags"), coreFolder)
 	cachedCoreFile := ctx.CoreBuildCachePath.Join(coreFileName, "core.a")
 	coreStatBefore, err := cachedCoreFile.Stat()
 	require.NoError(t, err)
diff --git a/legacy/builder/test/create_build_options_map_test.go b/legacy/builder/test/create_build_options_map_test.go
index ea62780ebe1..4e250b07d9b 100644
--- a/legacy/builder/test/create_build_options_map_test.go
+++ b/legacy/builder/test/create_build_options_map_test.go
@@ -22,6 +22,7 @@ import (
 	"github.com/arduino/arduino-cli/legacy/builder"
 	"github.com/arduino/arduino-cli/legacy/builder/types"
 	"github.com/arduino/go-paths-helper"
+	"github.com/arduino/go-properties-orderedmap"
 	"github.com/stretchr/testify/require"
 )
 
@@ -34,7 +35,7 @@ func TestCreateBuildOptionsMap(t *testing.T) {
 		FQBN:               parseFQBN(t, "my:nice:fqbn"),
 		Verbose:            true,
 		BuildPath:          paths.New("buildPath"),
-		OptimizationFlags:  "-Os",
+		BuildProperties:    properties.NewFromHashmap(map[string]string{"compiler.optimization_flags": "-Os"}),
 	}
 
 	create := builder.CreateBuildOptionsMap{}
diff --git a/legacy/builder/test/ctags_runner_test.go b/legacy/builder/test/ctags_runner_test.go
index 08b477e2911..013499a461c 100644
--- a/legacy/builder/test/ctags_runner_test.go
+++ b/legacy/builder/test/ctags_runner_test.go
@@ -16,7 +16,6 @@
 package test
 
 import (
-	"path/filepath"
 	"strings"
 	"testing"
 
@@ -27,37 +26,20 @@ import (
 )
 
 func TestCTagsRunner(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := Abs(t, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"))
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
-
 		&builder.PrintUsedLibrariesIfVerbose{},
 		&builder.WarnAboutArchIncompatibleLibraries{},
 		&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"},
 		&builder.CTagsRunner{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -76,37 +58,20 @@ func TestCTagsRunner(t *testing.T) {
 }
 
 func TestCTagsRunnerSketchWithClass(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := Abs(t, paths.New("sketch_with_class", "sketch_with_class.ino"))
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
-
 		&builder.PrintUsedLibrariesIfVerbose{},
 		&builder.WarnAboutArchIncompatibleLibraries{},
 		&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"},
 		&builder.CTagsRunner{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -118,42 +83,24 @@ func TestCTagsRunnerSketchWithClass(t *testing.T) {
 		"set_values\t" + quotedSketchLocation + "\t/^void Rectangle::set_values (int x, int y) {$/;\"\tkind:function\tline:8\tclass:Rectangle\tsignature:(int x, int y)\treturntype:void\n" +
 		"setup\t" + quotedSketchLocation + "\t/^void setup() {$/;\"\tkind:function\tline:13\tsignature:()\treturntype:void\n" +
 		"loop\t" + quotedSketchLocation + "\t/^void loop() {$/;\"\tkind:function\tline:17\tsignature:()\treturntype:void\n"
-
 	require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1))
 }
 
 func TestCTagsRunnerSketchWithTypename(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := Abs(t, paths.New("sketch_with_typename", "sketch_with_typename.ino"))
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
-
 		&builder.PrintUsedLibrariesIfVerbose{},
 		&builder.WarnAboutArchIncompatibleLibraries{},
 		&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"},
 		&builder.CTagsRunner{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -164,42 +111,24 @@ func TestCTagsRunnerSketchWithTypename(t *testing.T) {
 		"setup\t" + quotedSketchLocation + "\t/^void setup() {$/;\"\tkind:function\tline:6\tsignature:()\treturntype:void\n" +
 		"loop\t" + quotedSketchLocation + "\t/^void loop() {}$/;\"\tkind:function\tline:10\tsignature:()\treturntype:void\n" +
 		"func\t" + quotedSketchLocation + "\t/^typename Foo<char>::Bar func(){$/;\"\tkind:function\tline:12\tsignature:()\treturntype:Foo::Bar\n"
-
 	require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1))
 }
 
 func TestCTagsRunnerSketchWithNamespace(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := Abs(t, paths.New("sketch_with_namespace", "sketch_with_namespace.ino"))
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
-
 		&builder.PrintUsedLibrariesIfVerbose{},
 		&builder.WarnAboutArchIncompatibleLibraries{},
 		&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"},
 		&builder.CTagsRunner{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -209,42 +138,24 @@ func TestCTagsRunnerSketchWithNamespace(t *testing.T) {
 	expectedOutput := "value\t" + quotedSketchLocation + "\t/^\tint value() {$/;\"\tkind:function\tline:2\tnamespace:Test\tsignature:()\treturntype:int\n" +
 		"setup\t" + quotedSketchLocation + "\t/^void setup() {}$/;\"\tkind:function\tline:7\tsignature:()\treturntype:void\n" +
 		"loop\t" + quotedSketchLocation + "\t/^void loop() {}$/;\"\tkind:function\tline:8\tsignature:()\treturntype:void\n"
-
 	require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1))
 }
 
 func TestCTagsRunnerSketchWithTemplates(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := Abs(t, paths.New("sketch_with_templates_and_shift", "sketch_with_templates_and_shift.ino"))
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
-
 		&builder.PrintUsedLibrariesIfVerbose{},
 		&builder.WarnAboutArchIncompatibleLibraries{},
 		&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"},
 		&builder.CTagsRunner{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -255,6 +166,5 @@ func TestCTagsRunnerSketchWithTemplates(t *testing.T) {
 		"bVar\t" + quotedSketchLocation + "\t/^c< 8 > bVar;$/;\"\tkind:variable\tline:15\n" +
 		"aVar\t" + quotedSketchLocation + "\t/^c< 1<<8 > aVar;$/;\"\tkind:variable\tline:16\n" +
 		"func\t" + quotedSketchLocation + "\t/^template<int X> func( c< 1<<X> & aParam) {$/;\"\tkind:function\tline:18\tsignature:( c< 1<<X> & aParam)\treturntype:template\n"
-
 	require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1))
 }
diff --git a/legacy/builder/test/hardware_loader_test.go b/legacy/builder/test/hardware_loader_test.go
index e0cd7148b3a..f900ad38ac3 100644
--- a/legacy/builder/test/hardware_loader_test.go
+++ b/legacy/builder/test/hardware_loader_test.go
@@ -34,17 +34,10 @@ func TestLoadHardware(t *testing.T) {
 	ctx := &types.Context{
 		HardwareDirs: paths.NewPathList("downloaded_hardware", filepath.Join("..", "hardware")),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	packages := ctx.Hardware
+	packages := ctx.PackageManager.GetPackages()
 	require.Equal(t, 1, len(packages))
 	require.NotNil(t, packages["arduino"])
 	require.Equal(t, 2, len(packages["arduino"].Platforms))
@@ -67,29 +60,24 @@ func TestLoadHardware(t *testing.T) {
 	require.Equal(t, "/my/personal/avrdude", avrPlatform.Releases["1.6.10"].Properties.Get("tools.avrdude.cmd.path"))
 
 	require.Equal(t, "AVRISP mkII", avrPlatform.Releases["1.6.10"].Programmers["avrispmkii"].Name)
-
-	//require.Equal(t, "{runtime.tools.ctags.path}", packages.Properties.Get("tools.ctags.path"])
-	//require.Equal(t, "\"{cmd.path}\" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives \"{source_file}\"", packages.Properties.Get("tools.ctags.pattern"])
-	//require.Equal(t, "{runtime.tools.avrdude.path}", packages.Properties.Get("tools.avrdude.path"])
-	//require.Equal(t, "-w -x c++ -E -CC", packages.Properties.Get("preproc.macros.flags"])
 }
 
 func TestLoadHardwareMixingUserHardwareFolder(t *testing.T) {
 	ctx := &types.Context{
 		HardwareDirs: paths.NewPathList("downloaded_hardware", filepath.Join("..", "hardware"), "user_hardware"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.AddAdditionalEntriesToContext{},
-		&builder.HardwareLoader{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
 	}
 
-	packages := ctx.Hardware
+	packages := ctx.PackageManager.GetPackages()
 
 	if runtime.GOOS == "windows" {
 		//a package is a symlink, and windows does not support them
@@ -146,17 +134,10 @@ func TestLoadHardwareWithBoardManagerFolderStructure(t *testing.T) {
 	ctx := &types.Context{
 		HardwareDirs: paths.NewPathList("downloaded_board_manager_stuff"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	packages := ctx.Hardware
+	packages := ctx.PackageManager.GetPackages()
 	require.Equal(t, 3, len(packages))
 	require.NotNil(t, packages["arduino"])
 	require.Equal(t, 1, len(packages["arduino"].Platforms))
@@ -194,17 +175,10 @@ func TestLoadLotsOfHardware(t *testing.T) {
 	ctx := &types.Context{
 		HardwareDirs: paths.NewPathList("downloaded_board_manager_stuff", "downloaded_hardware", filepath.Join("..", "hardware"), "user_hardware"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	packages := ctx.Hardware
+	packages := ctx.PackageManager.GetPackages()
 
 	if runtime.GOOS == "windows" {
 		//a package is a symlink, and windows does not support them
diff --git a/legacy/builder/test/helper.go b/legacy/builder/test/helper.go
index 38144840f1f..4581dd7c921 100644
--- a/legacy/builder/test/helper.go
+++ b/legacy/builder/test/helper.go
@@ -23,8 +23,8 @@ import (
 	"testing"
 	"text/template"
 
+	"github.com/arduino/arduino-cli/arduino/cores"
 	"github.com/arduino/arduino-cli/arduino/libraries"
-	"github.com/arduino/arduino-cli/arduino/sketch"
 	"github.com/arduino/arduino-cli/legacy/builder/constants"
 	"github.com/arduino/arduino-cli/legacy/builder/types"
 	"github.com/arduino/arduino-cli/legacy/builder/utils"
@@ -77,10 +77,10 @@ func SetupBuildCachePath(t *testing.T, ctx *types.Context) *paths.Path {
 	return buildCachePath
 }
 
-func OpenSketch(t *testing.T, sketchPath *paths.Path) *sketch.Sketch {
-	sketch, err := sketch.New(sketchPath)
+func parseFQBN(t *testing.T, fqbnIn string) *cores.FQBN {
+	fqbn, err := cores.ParseFQBN(fqbnIn)
 	require.NoError(t, err)
-	return sketch
+	return fqbn
 }
 
 type ByLibraryName []*libraries.Library
diff --git a/legacy/builder/test/includes_to_include_folders_test.go b/legacy/builder/test/includes_to_include_folders_test.go
index e7deabaddb1..537b58d327e 100644
--- a/legacy/builder/test/includes_to_include_folders_test.go
+++ b/legacy/builder/test/includes_to_include_folders_test.go
@@ -27,30 +27,15 @@ import (
 )
 
 func TestIncludesToIncludeFolders(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -62,30 +47,15 @@ func TestIncludesToIncludeFolders(t *testing.T) {
 }
 
 func TestIncludesToIncludeFoldersSketchWithIfDef(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("SketchWithIfDef", "SketchWithIfDef.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("SketchWithIfDef", "SketchWithIfDef.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -96,30 +66,15 @@ func TestIncludesToIncludeFoldersSketchWithIfDef(t *testing.T) {
 }
 
 func TestIncludesToIncludeFoldersIRremoteLibrary(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch9", "sketch9.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch9", "sketch9.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -133,27 +88,15 @@ func TestIncludesToIncludeFoldersIRremoteLibrary(t *testing.T) {
 }
 
 func TestIncludesToIncludeFoldersANewLibrary(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch10", "sketch10.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch10", "sketch10.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -167,29 +110,20 @@ func TestIncludesToIncludeFoldersANewLibrary(t *testing.T) {
 }
 
 func TestIncludesToIncludeFoldersDuplicateLibs(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "user_hardware"),
 		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		Sketch:               OpenSketch(t, paths.New("user_hardware", "my_avr_platform", "avr", "libraries", "SPI", "examples", "BarometricPressureSensor", "BarometricPressureSensor.ino")),
-		FQBN:                 parseFQBN(t, "my_avr_platform:avr:custom_yun"),
 		Verbose:              true,
 	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("user_hardware", "my_avr_platform", "avr", "libraries", "SPI", "examples", "BarometricPressureSensor", "BarometricPressureSensor.ino"), "my_avr_platform:avr:custom_yun")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -203,30 +137,21 @@ func TestIncludesToIncludeFoldersDuplicateLibs(t *testing.T) {
 }
 
 func TestIncludesToIncludeFoldersDuplicateLibsWithConflictingLibsOutsideOfPlatform(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "user_hardware"),
 		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
 		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("user_hardware", "my_avr_platform", "avr", "libraries", "SPI", "examples", "BarometricPressureSensor", "BarometricPressureSensor.ino")),
-		FQBN:                 parseFQBN(t, "my_avr_platform:avr:custom_yun"),
 		Verbose:              true,
 	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("user_hardware", "my_avr_platform", "avr", "libraries", "SPI", "examples", "BarometricPressureSensor", "BarometricPressureSensor.ino"), "my_avr_platform:avr:custom_yun")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -240,30 +165,21 @@ func TestIncludesToIncludeFoldersDuplicateLibsWithConflictingLibsOutsideOfPlatfo
 }
 
 func TestIncludesToIncludeFoldersDuplicateLibs2(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "downloaded_board_manager_stuff"),
 		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
 		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch_usbhost", "sketch_usbhost.ino")),
-		FQBN:                 parseFQBN(t, "arduino:samd:arduino_zero_native"),
 		Verbose:              true,
 	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch_usbhost", "sketch_usbhost.ino"), "arduino:samd:arduino_zero_native")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -277,30 +193,15 @@ func TestIncludesToIncludeFoldersDuplicateLibs2(t *testing.T) {
 }
 
 func TestIncludesToIncludeFoldersSubfolders(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch_with_subfolders", "sketch_with_subfolders.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch_with_subfolders", "sketch_with_subfolders.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
+	ctx.Verbose = true
 
 	commands := []types.Command{
-
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
-
 		&builder.ContainerMergeCopySketchFiles{},
-
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
diff --git a/legacy/builder/test/libraries_loader_test.go b/legacy/builder/test/libraries_loader_test.go
index 4e1ca1a28f7..143c1866221 100644
--- a/legacy/builder/test/libraries_loader_test.go
+++ b/legacy/builder/test/libraries_loader_test.go
@@ -39,22 +39,18 @@ func extractLibraries(ctx *types.Context) []*libraries.Library {
 }
 
 func TestLoadLibrariesAVR(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
 		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.AddAdditionalEntriesToContext{},
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
 		&builder.LibrariesLoader{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -149,22 +145,18 @@ func TestLoadLibrariesAVR(t *testing.T) {
 }
 
 func TestLoadLibrariesSAM(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
 		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		FQBN:                 parseFQBN(t, "arduino:sam:arduino_due_x_dbg"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "arduino:sam:arduino_due_x_dbg")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.AddAdditionalEntriesToContext{},
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
 		&builder.LibrariesLoader{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -232,22 +224,18 @@ func TestLoadLibrariesSAM(t *testing.T) {
 }
 
 func TestLoadLibrariesAVRNoDuplicateLibrariesFolders(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
 		OtherLibrariesDirs:   paths.NewPathList("libraries", filepath.Join("downloaded_hardware", "arduino", "avr", "libraries")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.AddAdditionalEntriesToContext{},
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
 		&builder.LibrariesLoader{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -261,22 +249,18 @@ func TestLoadLibrariesAVRNoDuplicateLibrariesFolders(t *testing.T) {
 }
 
 func TestLoadLibrariesMyAVRPlatform(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "user_hardware", "downloaded_hardware"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
 		OtherLibrariesDirs:   paths.NewPathList("libraries", filepath.Join("downloaded_hardware", "arduino", "avr", "libraries")),
-		FQBN:                 parseFQBN(t, "my_avr_platform:avr:custom_yun"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "my_avr_platform:avr:custom_yun")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.AddAdditionalEntriesToContext{},
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
 		&builder.LibrariesLoader{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
diff --git a/legacy/builder/test/merge_sketch_with_bootloader_test.go b/legacy/builder/test/merge_sketch_with_bootloader_test.go
index 47c1b4d545c..b36102f6c40 100644
--- a/legacy/builder/test/merge_sketch_with_bootloader_test.go
+++ b/legacy/builder/test/merge_sketch_with_bootloader_test.go
@@ -29,20 +29,10 @@ import (
 )
 
 func TestMergeSketchWithBootloader(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch1", "sketch1.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:uno"),
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
+	buildPath := ctx.BuildPath
 	err := buildPath.Join("sketch").MkdirAll()
 	NoError(t, err)
 
@@ -98,20 +88,10 @@ func TestMergeSketchWithBootloader(t *testing.T) {
 }
 
 func TestMergeSketchWithBootloaderSketchInBuildPath(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch1", "sketch1.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:uno"),
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
+	buildPath := ctx.BuildPath
 	err := buildPath.Join("sketch").MkdirAll()
 	NoError(t, err)
 
@@ -168,20 +148,10 @@ func TestMergeSketchWithBootloaderSketchInBuildPath(t *testing.T) {
 }
 
 func TestMergeSketchWithBootloaderWhenNoBootloaderAvailable(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch1", "sketch1.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:uno"),
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
+	buildPath := ctx.BuildPath
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 	}
@@ -205,20 +175,16 @@ func TestMergeSketchWithBootloaderWhenNoBootloaderAvailable(t *testing.T) {
 }
 
 func TestMergeSketchWithBootloaderPathIsParameterized(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "user_hardware"),
 		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
 		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch1", "sketch1.ino")),
-		FQBN:                 parseFQBN(t, "my_avr_platform:avr:mymega:cpu=atmega2560"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch1", "sketch1.ino"), "my_avr_platform:avr:mymega:cpu=atmega2560")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
-
+	buildPath := ctx.BuildPath
 	err := buildPath.Join("sketch").MkdirAll()
 	NoError(t, err)
 
diff --git a/legacy/builder/test/prototypes_adder_test.go b/legacy/builder/test/prototypes_adder_test.go
index 6d88f2ce9d3..67989677ea0 100644
--- a/legacy/builder/test/prototypes_adder_test.go
+++ b/legacy/builder/test/prototypes_adder_test.go
@@ -29,30 +29,19 @@ import (
 )
 
 func TestPrototypesAdderBridgeExample(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -64,27 +53,16 @@ func TestPrototypesAdderBridgeExample(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithIfDef(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("SketchWithIfDef", "SketchWithIfDef.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, paths.New("SketchWithIfDef", "SketchWithIfDef.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -96,27 +74,16 @@ func TestPrototypesAdderSketchWithIfDef(t *testing.T) {
 }
 
 func TestPrototypesAdderBaladuino(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("Baladuino", "Baladuino.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, paths.New("Baladuino", "Baladuino.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -128,27 +95,16 @@ func TestPrototypesAdderBaladuino(t *testing.T) {
 }
 
 func TestPrototypesAdderCharWithEscapedDoubleQuote(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("CharWithEscapedDoubleQuote", "CharWithEscapedDoubleQuote.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, paths.New("CharWithEscapedDoubleQuote", "CharWithEscapedDoubleQuote.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -160,27 +116,16 @@ func TestPrototypesAdderCharWithEscapedDoubleQuote(t *testing.T) {
 }
 
 func TestPrototypesAdderIncludeBetweenMultilineComment(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("IncludeBetweenMultilineComment", "IncludeBetweenMultilineComment.ino")),
-		FQBN:                 parseFQBN(t, "arduino:sam:arduino_due_x_dbg"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, paths.New("IncludeBetweenMultilineComment", "IncludeBetweenMultilineComment.ino"), "arduino:sam:arduino_due_x_dbg")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -192,27 +137,16 @@ func TestPrototypesAdderIncludeBetweenMultilineComment(t *testing.T) {
 }
 
 func TestPrototypesAdderLineContinuations(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
+	ctx := prepareBuilderTestContext(t, nil, paths.New("LineContinuations", "LineContinuations.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("LineContinuations", "LineContinuations.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -224,27 +158,16 @@ func TestPrototypesAdderLineContinuations(t *testing.T) {
 }
 
 func TestPrototypesAdderStringWithComment(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("StringWithComment", "StringWithComment.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, paths.New("StringWithComment", "StringWithComment.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -256,27 +179,16 @@ func TestPrototypesAdderStringWithComment(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithStruct(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("SketchWithStruct", "SketchWithStruct.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, paths.New("SketchWithStruct", "SketchWithStruct.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -293,30 +205,19 @@ func TestPrototypesAdderSketchWithStruct(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithConfig(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_with_config", "sketch_with_config.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -331,30 +232,19 @@ func TestPrototypesAdderSketchWithConfig(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchNoFunctionsTwoFiles(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_no_functions_two_files", "sketch_no_functions_two_files.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch_no_functions_two_files", "sketch_no_functions_two_files.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch_no_functions_two_files", "sketch_no_functions_two_files.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -366,20 +256,10 @@ func TestPrototypesAdderSketchNoFunctionsTwoFiles(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchNoFunctions(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
+	ctx := prepareBuilderTestContext(t, nil, paths.New("sketch_no_functions", "sketch_no_functions.ino"), "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("sketch_no_functions", "sketch_no_functions.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	sketchLocation := paths.New("sketch_no_functions", "sketch_no_functions.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
@@ -389,7 +269,6 @@ func TestPrototypesAdderSketchNoFunctions(t *testing.T) {
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -401,30 +280,19 @@ func TestPrototypesAdderSketchNoFunctions(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithDefaultArgs(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_with_default_args", "sketch_with_default_args.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -436,30 +304,19 @@ func TestPrototypesAdderSketchWithDefaultArgs(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithInlineFunction(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_with_inline_function", "sketch_with_inline_function.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -482,30 +339,19 @@ func TestPrototypesAdderSketchWithInlineFunction(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithFunctionSignatureInsideIFDEF(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_with_function_signature_inside_ifdef", "sketch_with_function_signature_inside_ifdef.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -517,8 +363,6 @@ func TestPrototypesAdderSketchWithFunctionSignatureInsideIFDEF(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithUSBCON(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_with_usbcon", "sketch_with_usbcon.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
@@ -527,20 +371,16 @@ func TestPrototypesAdderSketchWithUSBCON(t *testing.T) {
 		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
 		OtherLibrariesDirs:   paths.NewPathList("libraries"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
 		Verbose:              true,
 	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx = prepareBuilderTestContext(t, ctx, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -552,8 +392,6 @@ func TestPrototypesAdderSketchWithUSBCON(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithTypename(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_with_typename", "sketch_with_typename.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
@@ -561,20 +399,16 @@ func TestPrototypesAdderSketchWithTypename(t *testing.T) {
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
 		BuiltInLibrariesDirs: paths.New("libraries", "downloaded_libraries"),
 		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
 		Verbose:              true,
 	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx = prepareBuilderTestContext(t, ctx, sketchLocation, "arduino:avr:leonardo")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -592,30 +426,19 @@ func TestPrototypesAdderSketchWithTypename(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithIfDef2(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_with_ifdef", "sketch_with_ifdef.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:yun"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:yun")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -630,30 +453,19 @@ func TestPrototypesAdderSketchWithIfDef2(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithIfDef2SAM(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_with_ifdef", "sketch_with_ifdef.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:sam:arduino_due_x_dbg"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:sam:arduino_due_x_dbg")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -668,30 +480,19 @@ func TestPrototypesAdderSketchWithIfDef2SAM(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithConst(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	sketchLocation := paths.New("sketch_with_const", "sketch_with_const.ino")
 	quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation))
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:uno"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -703,27 +504,16 @@ func TestPrototypesAdderSketchWithConst(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithDosEol(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, paths.New("eol_processing", "eol_processing.ino")),
-		FQBN:                 parseFQBN(t, "arduino:avr:uno"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, paths.New("eol_processing", "eol_processing.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -733,29 +523,19 @@ func TestPrototypesAdderSketchWithDosEol(t *testing.T) {
 }
 
 func TestPrototypesAdderSketchWithSubstringFunctionMember(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
 	sketchLocation := paths.New("sketch_with_class_and_method_substring", "sketch_with_class_and_method_substring.ino")
 	quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation).String())
 
-	ctx := &types.Context{
-		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
-		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
-		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		Sketch:               OpenSketch(t, sketchLocation),
-		FQBN:                 parseFQBN(t, "arduino:avr:uno"),
-		Verbose:              true,
-	}
+	ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx.Verbose = true
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
 		&builder.ContainerMergeCopySketchFiles{},
 		&builder.ContainerFindIncludes{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
diff --git a/legacy/builder/test/setup_build_properties_test.go b/legacy/builder/test/setup_build_properties_test.go
index f59847d81ff..1b595d5d69c 100644
--- a/legacy/builder/test/setup_build_properties_test.go
+++ b/legacy/builder/test/setup_build_properties_test.go
@@ -22,29 +22,21 @@ import (
 	"github.com/arduino/arduino-cli/legacy/builder"
 	"github.com/arduino/arduino-cli/legacy/builder/types"
 	paths "github.com/arduino/go-paths-helper"
+	"github.com/arduino/go-properties-orderedmap"
 	"github.com/stretchr/testify/require"
 )
 
 func TestSetupBuildProperties(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:     paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "user_hardware"),
 		BuiltInToolsDirs: paths.NewPathList("downloaded_tools", "tools_builtin"),
-		Sketch:           OpenSketch(t, paths.New("sketch1", "sketch1.ino")),
-		FQBN:             parseFQBN(t, "arduino:avr:uno"),
 	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.AddAdditionalEntriesToContext{},
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-		&builder.SetupBuildProperties{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -89,37 +81,19 @@ func TestSetupBuildProperties(t *testing.T) {
 }
 
 func TestSetupBuildPropertiesWithSomeCustomOverrides(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
-		HardwareDirs:     paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		BuiltInToolsDirs: paths.NewPathList("downloaded_tools", "tools_builtin"),
-		Sketch:           OpenSketch(t, paths.New("sketch1", "sketch1.ino")),
-		FQBN:             parseFQBN(t, "arduino:avr:uno"),
-
+		HardwareDirs:          paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
+		BuiltInToolsDirs:      paths.NewPathList("downloaded_tools", "tools_builtin"),
 		CustomBuildProperties: []string{"name=fake name", "tools.avrdude.config.path=non existent path with space and a ="},
 	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
-
-	commands := []types.Command{
-		&builder.AddAdditionalEntriesToContext{},
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-		&builder.SetupBuildProperties{},
-		&builder.SetCustomBuildProperties{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
+	defer cleanUpBuilderTestContext(t, ctx)
+	customProps, err := properties.LoadFromSlice(ctx.CustomBuildProperties)
+	NoError(t, err)
+	ctx.BuildProperties.Merge(customProps)
 
 	buildProperties := ctx.BuildProperties
-
 	require.Equal(t, "ARDUINO", buildProperties.Get("software"))
-
 	require.Equal(t, "uno", buildProperties.Get("_id"))
 	require.Equal(t, "fake name", buildProperties.Get("name"))
 	require.Equal(t, "\"{compiler.path}{compiler.c.cmd}\" {compiler.c.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} \"{source_file}\" -o \"{object_file}\"", buildProperties.Get("recipe.c.o.pattern"))
@@ -127,25 +101,16 @@ func TestSetupBuildPropertiesWithSomeCustomOverrides(t *testing.T) {
 }
 
 func TestSetupBuildPropertiesUserHardware(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:     paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "user_hardware"),
 		BuiltInToolsDirs: paths.NewPathList("downloaded_tools", "tools_builtin"),
-		Sketch:           OpenSketch(t, paths.New("sketch1", "sketch1.ino")),
-		FQBN:             parseFQBN(t, "my_avr_platform:avr:custom_yun"),
 	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch1", "sketch1.ino"), "my_avr_platform:avr:custom_yun")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.AddAdditionalEntriesToContext{},
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-		&builder.SetupBuildProperties{},
 	}
-
 	for _, command := range commands {
 		err := command.Run(ctx)
 		NoError(t, err)
@@ -162,17 +127,12 @@ func TestSetupBuildPropertiesUserHardware(t *testing.T) {
 }
 
 func TestSetupBuildPropertiesWithMissingPropsFromParentPlatformTxtFiles(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:     paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "user_hardware"),
 		BuiltInToolsDirs: paths.NewPathList("downloaded_tools", "tools_builtin"),
-		Sketch:           OpenSketch(t, paths.New("sketch1", "sketch1.ino")),
-		FQBN:             parseFQBN(t, "my_avr_platform:avr:custom_yun"),
 	}
-
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
+	ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch1", "sketch1.ino"), "my_avr_platform:avr:custom_yun")
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	commands := []types.Command{
 		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
diff --git a/legacy/builder/test/store_build_options_map_test.go b/legacy/builder/test/store_build_options_map_test.go
index aeee52e5154..47d040f9dac 100644
--- a/legacy/builder/test/store_build_options_map_test.go
+++ b/legacy/builder/test/store_build_options_map_test.go
@@ -23,6 +23,7 @@ import (
 	"github.com/arduino/arduino-cli/legacy/builder/constants"
 	"github.com/arduino/arduino-cli/legacy/builder/types"
 	paths "github.com/arduino/go-paths-helper"
+	"github.com/arduino/go-properties-orderedmap"
 	"github.com/stretchr/testify/require"
 )
 
@@ -36,7 +37,7 @@ func TestStoreBuildOptionsMap(t *testing.T) {
 		FQBN:                  parseFQBN(t, "my:nice:fqbn"),
 		CustomBuildProperties: []string{"custom=prop"},
 		Verbose:               true,
-		OptimizationFlags:     "-Os",
+		BuildProperties:       properties.NewFromHashmap(map[string]string{"compiler.optimization_flags": "-Os"}),
 	}
 
 	buildPath := SetupBuildPath(t, ctx)
diff --git a/legacy/builder/test/target_board_resolver_test.go b/legacy/builder/test/target_board_resolver_test.go
deleted file mode 100644
index 2ce650f081d..00000000000
--- a/legacy/builder/test/target_board_resolver_test.go
+++ /dev/null
@@ -1,186 +0,0 @@
-// This file is part of arduino-cli.
-//
-// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
-//
-// This software is released under the GNU General Public License version 3,
-// which covers the main part of arduino-cli.
-// The terms of this license can be found at:
-// https://www.gnu.org/licenses/gpl-3.0.en.html
-//
-// You can be released from the requirements of the above licenses by purchasing
-// a commercial license. Buying such a license is mandatory if you want to
-// modify or otherwise use the software for commercial activities involving the
-// Arduino software without disclosing the source code of your own applications.
-// To purchase a commercial license, send an email to license@arduino.cc.
-
-package test
-
-import (
-	"path/filepath"
-	"testing"
-
-	"github.com/arduino/arduino-cli/legacy/builder"
-	"github.com/arduino/arduino-cli/legacy/builder/types"
-	paths "github.com/arduino/go-paths-helper"
-	"github.com/stretchr/testify/require"
-)
-
-func TestTargetBoardResolverUno(t *testing.T) {
-	ctx := &types.Context{
-		HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		FQBN:         parseFQBN(t, "arduino:avr:uno"),
-	}
-
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	targetPackage := ctx.TargetPackage
-	require.Equal(t, "arduino", targetPackage.Name)
-	targetPlatform := ctx.TargetPlatform
-	require.Equal(t, "avr", targetPlatform.Platform.Architecture)
-	targetBoard := ctx.TargetBoard
-	require.Equal(t, "uno", targetBoard.BoardID)
-	targetBoardBuildProperties := ctx.TargetBoardBuildProperties
-	require.Equal(t, "atmega328p", targetBoardBuildProperties.Get("build.mcu"))
-}
-
-func TestTargetBoardResolverDue(t *testing.T) {
-	ctx := &types.Context{
-		HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		FQBN:         parseFQBN(t, "arduino:sam:arduino_due_x"),
-	}
-
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	targetPackage := ctx.TargetPackage
-	require.Equal(t, "arduino", targetPackage.Name)
-	targetPlatform := ctx.TargetPlatform
-	require.Equal(t, "sam", targetPlatform.Platform.Architecture)
-	targetBoard := ctx.TargetBoard
-	require.Equal(t, "arduino_due_x", targetBoard.BoardID)
-	targetBoardBuildProperties := ctx.TargetBoardBuildProperties
-	require.Equal(t, "cortex-m3", targetBoardBuildProperties.Get("build.mcu"))
-}
-
-func TestTargetBoardResolverMega1280(t *testing.T) {
-	ctx := &types.Context{
-		HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		FQBN:         parseFQBN(t, "arduino:avr:mega:cpu=atmega1280"),
-	}
-
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	targetPackage := ctx.TargetPackage
-	require.Equal(t, "arduino", targetPackage.Name)
-	targetPlatform := ctx.TargetPlatform
-	require.Equal(t, "avr", targetPlatform.Platform.Architecture)
-	targetBoard := ctx.TargetBoard
-	require.Equal(t, "mega", targetBoard.BoardID)
-	targetBoardBuildProperties := ctx.TargetBoardBuildProperties
-	require.Equal(t, "atmega1280", targetBoardBuildProperties.Get("build.mcu"))
-	require.Equal(t, "AVR_MEGA", targetBoardBuildProperties.Get("build.board"))
-}
-
-func TestTargetBoardResolverMega2560(t *testing.T) {
-	ctx := &types.Context{
-		HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
-		FQBN:         parseFQBN(t, "arduino:avr:mega:cpu=atmega2560"),
-	}
-
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	targetPackage := ctx.TargetPackage
-	require.Equal(t, "arduino", targetPackage.Name)
-	targetPlatform := ctx.TargetPlatform
-	require.Equal(t, "avr", targetPlatform.Platform.Architecture)
-	targetBoard := ctx.TargetBoard
-	require.Equal(t, "mega", targetBoard.BoardID)
-	targetBoardBuildProperties := ctx.TargetBoardBuildProperties
-	require.Equal(t, "atmega2560", targetBoardBuildProperties.Get("build.mcu"))
-	require.Equal(t, "AVR_MEGA2560", targetBoardBuildProperties.Get("build.board"))
-}
-
-func TestTargetBoardResolverCustomYun(t *testing.T) {
-	ctx := &types.Context{
-		HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "user_hardware"),
-		FQBN:         parseFQBN(t, "my_avr_platform:avr:custom_yun"),
-	}
-
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	targetPackage := ctx.TargetPackage
-	require.Equal(t, "my_avr_platform", targetPackage.Name)
-	targetPlatform := ctx.TargetPlatform
-	require.Equal(t, "avr", targetPlatform.Platform.Architecture)
-	targetBoard := ctx.TargetBoard
-	require.Equal(t, "custom_yun", targetBoard.BoardID)
-	targetBoardBuildProperties := ctx.TargetBoardBuildProperties
-	require.Equal(t, "atmega32u4", targetBoardBuildProperties.Get("build.mcu"))
-	require.Equal(t, "AVR_YUN", targetBoardBuildProperties.Get("build.board"))
-}
-
-func TestTargetBoardResolverCustomCore(t *testing.T) {
-	ctx := &types.Context{
-		HardwareDirs: paths.NewPathList("hardware"),
-		FQBN:         parseFQBN(t, "watterott:avr:attiny841:core=spencekonde,info=info"),
-	}
-
-	commands := []types.Command{
-		&builder.HardwareLoader{},
-		&builder.TargetBoardResolver{},
-	}
-
-	for _, command := range commands {
-		err := command.Run(ctx)
-		NoError(t, err)
-	}
-
-	targetPackage := ctx.TargetPackage
-	require.Equal(t, "watterott", targetPackage.Name)
-	targetPlatform := ctx.TargetPlatform
-	require.Equal(t, "avr", targetPlatform.Platform.Architecture)
-	targetBoard := ctx.TargetBoard
-	require.Equal(t, "attiny841", targetBoard.BoardID)
-	require.Equal(t, "tiny841", ctx.TargetBoardBuildProperties.Get("build.core"))
-	targetBoardBuildProperties := ctx.TargetBoardBuildProperties
-	require.Equal(t, "tiny14", targetBoardBuildProperties.Get("build.variant"))
-}
diff --git a/legacy/builder/test/tools_loader_test.go b/legacy/builder/test/tools_loader_test.go
index 2a40f69f168..7138df09afc 100644
--- a/legacy/builder/test/tools_loader_test.go
+++ b/legacy/builder/test/tools_loader_test.go
@@ -16,12 +16,12 @@
 package test
 
 import (
+	"path/filepath"
 	"sort"
 	"testing"
 
 	"github.com/arduino/arduino-cli/arduino/cores"
 	"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
-	"github.com/arduino/arduino-cli/legacy/builder"
 	"github.com/arduino/arduino-cli/legacy/builder/types"
 	paths "github.com/arduino/go-paths-helper"
 	"github.com/stretchr/testify/require"
@@ -62,15 +62,14 @@ func requireEquivalentPaths(t *testing.T, actual string, expected ...string) {
 }
 
 func TestLoadTools(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
+		HardwareDirs:     paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"),
 		BuiltInToolsDirs: paths.NewPathList("downloaded_tools", "tools_builtin"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	NoError(t, (&builder.HardwareLoader{}).Run(ctx))
-
-	tools := ctx.AllTools
+	tools := ctx.PackageManager.GetAllInstalledToolsReleases()
 	require.Equal(t, 9, len(tools))
 
 	sort.Sort(ByToolIDAndVersion(tools))
@@ -105,14 +104,13 @@ func TestLoadTools(t *testing.T) {
 }
 
 func TestLoadToolsWithBoardManagerFolderStructure(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
 	ctx := &types.Context{
 		HardwareDirs: paths.NewPathList("downloaded_board_manager_stuff"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	NoError(t, (&builder.HardwareLoader{}).Run(ctx))
-
-	tools := ctx.AllTools
+	tools := ctx.PackageManager.GetAllInstalledToolsReleases()
 	require.Equal(t, 3, len(tools))
 
 	sort.Sort(ByToolIDAndVersion(tools))
@@ -129,16 +127,14 @@ func TestLoadToolsWithBoardManagerFolderStructure(t *testing.T) {
 }
 
 func TestLoadLotsOfTools(t *testing.T) {
-	DownloadCoresAndToolsAndLibraries(t)
-
 	ctx := &types.Context{
 		HardwareDirs:     paths.NewPathList("downloaded_board_manager_stuff"),
 		BuiltInToolsDirs: paths.NewPathList("downloaded_tools", "tools_builtin"),
 	}
+	ctx = prepareBuilderTestContext(t, ctx, nil, "")
+	defer cleanUpBuilderTestContext(t, ctx)
 
-	NoError(t, (&builder.HardwareLoader{}).Run(ctx))
-
-	tools := ctx.AllTools
+	tools := ctx.PackageManager.GetAllInstalledToolsReleases()
 	require.Equal(t, 12, len(tools))
 
 	sort.Sort(ByToolIDAndVersion(tools))
@@ -193,7 +189,5 @@ func TestAllToolsContextIsPopulated(t *testing.T) {
 		PackageManager: pme,
 	}
 
-	hl := &builder.HardwareLoader{}
-	require.NoError(t, hl.Run(ctx))
-	require.NotEmpty(t, ctx.AllTools)
+	require.NotEmpty(t, ctx.PackageManager.GetAllInstalledToolsReleases())
 }
diff --git a/legacy/builder/test/try_build_of_problematic_sketch_test.go b/legacy/builder/test/try_build_of_problematic_sketch_test.go
index c8b61a3db74..c82c6e0b290 100644
--- a/legacy/builder/test/try_build_of_problematic_sketch_test.go
+++ b/legacy/builder/test/try_build_of_problematic_sketch_test.go
@@ -26,223 +26,211 @@ import (
 )
 
 func TestTryBuild001(t *testing.T) {
-	tryBuild(t, "sketch_with_inline_function", "sketch_with_inline_function.ino")
+	tryBuild(t, paths.New("sketch_with_inline_function", "sketch_with_inline_function.ino"))
 }
 
 func TestTryBuild002(t *testing.T) {
-	tryBuild(t, "sketch_with_function_signature_inside_ifdef", "sketch_with_function_signature_inside_ifdef.ino")
+	tryBuild(t, paths.New("sketch_with_function_signature_inside_ifdef", "sketch_with_function_signature_inside_ifdef.ino"))
 }
 
 func TestTryBuild003(t *testing.T) {
-	tryPreprocess(t, "sketch_no_functions", "sketch_no_functions.ino")
+	tryPreprocess(t, paths.New("sketch_no_functions", "sketch_no_functions.ino"))
 }
 
 func TestTryBuild004(t *testing.T) {
-	tryBuild(t, "sketch_with_const", "sketch_with_const.ino")
+	tryBuild(t, paths.New("sketch_with_const", "sketch_with_const.ino"))
 }
 
 func TestTryBuild005(t *testing.T) {
-	tryBuild(t, "sketch_with_old_lib", "sketch_with_old_lib.ino")
+	tryBuild(t, paths.New("sketch_with_old_lib", "sketch_with_old_lib.ino"))
 }
 
 func TestTryBuild006(t *testing.T) {
-	tryBuild(t, "sketch_with_macosx_garbage", "sketch_with_macosx_garbage.ino")
+	tryBuild(t, paths.New("sketch_with_macosx_garbage", "sketch_with_macosx_garbage.ino"))
 }
 
 func TestTryBuild007(t *testing.T) {
-	tryBuild(t, "sketch_with_config", "sketch_with_config.ino")
+	tryBuild(t, paths.New("sketch_with_config", "sketch_with_config.ino"))
 }
 
 // XXX: Failing sketch, typename not supported
 //func TestTryBuild008(t *testing.T) {
-//	tryBuild(t, "sketch_with_typename", "sketch.ino")
+//	tryBuild(t, paths.New("sketch_with_typename", "sketch.ino"))
 //}
 
 func TestTryBuild009(t *testing.T) {
-	tryBuild(t, "sketch_with_usbcon", "sketch_with_usbcon.ino")
+	tryBuild(t, paths.New("sketch_with_usbcon", "sketch_with_usbcon.ino"))
 }
 
 func TestTryBuild010(t *testing.T) {
-	tryBuild(t, "sketch_with_namespace", "sketch_with_namespace.ino")
+	tryBuild(t, paths.New("sketch_with_namespace", "sketch_with_namespace.ino"))
 }
 
 func TestTryBuild011(t *testing.T) {
-	tryBuild(t, "sketch_with_inline_function", "sketch_with_inline_function.ino")
+	tryBuild(t, paths.New("sketch_with_inline_function", "sketch_with_inline_function.ino"))
 }
 
 func TestTryBuild012(t *testing.T) {
-	tryBuild(t, "sketch_with_default_args", "sketch_with_default_args.ino")
+	tryBuild(t, paths.New("sketch_with_default_args", "sketch_with_default_args.ino"))
 }
 
 func TestTryBuild013(t *testing.T) {
-	tryBuild(t, "sketch_with_class", "sketch_with_class.ino")
+	tryBuild(t, paths.New("sketch_with_class", "sketch_with_class.ino"))
 }
 
 func TestTryBuild014(t *testing.T) {
-	tryBuild(t, "sketch_with_backup_files", "sketch_with_backup_files.ino")
+	tryBuild(t, paths.New("sketch_with_backup_files", "sketch_with_backup_files.ino"))
 }
 
 func TestTryBuild015(t *testing.T) {
-	tryBuild(t, "sketch_with_subfolders")
+	tryBuild(t, paths.New("sketch_with_subfolders"))
 }
 
 // This is a sketch that fails to build on purpose
 //func TestTryBuild016(t *testing.T) {
-//	tryBuild(t, "sketch_that_checks_if_SPI_has_transactions_and_includes_missing_Ethernet", "sketch.ino")
+//	tryBuild(t, paths.New("sketch_that_checks_if_SPI_has_transactions_and_includes_missing_Ethernet", "sketch.ino"))
 //}
 
 func TestTryBuild017(t *testing.T) {
-	tryPreprocess(t, "sketch_no_functions_two_files", "sketch_no_functions_two_files.ino")
+	tryPreprocess(t, paths.New("sketch_no_functions_two_files", "sketch_no_functions_two_files.ino"))
 }
 
 func TestTryBuild018(t *testing.T) {
-	tryBuild(t, "sketch_that_checks_if_SPI_has_transactions", "sketch_that_checks_if_SPI_has_transactions.ino")
+	tryBuild(t, paths.New("sketch_that_checks_if_SPI_has_transactions", "sketch_that_checks_if_SPI_has_transactions.ino"))
 }
 
 func TestTryBuild019(t *testing.T) {
-	tryBuild(t, "sketch_with_ifdef", "sketch_with_ifdef.ino")
+	tryBuild(t, paths.New("sketch_with_ifdef", "sketch_with_ifdef.ino"))
 }
 
 func TestTryBuild020(t *testing.T) {
-	ctx := makeDefaultContext(t)
+	ctx := makeDefaultContext()
 	ctx.OtherLibrariesDirs = paths.NewPathList("dependent_libraries", "libraries")
-	tryPreprocessWithContext(t, ctx, "sketch_with_dependend_libraries", "sketch_with_dependend_libraries.ino")
+	tryPreprocessWithContext(t, ctx, "arduino:avr:leonardo", paths.New("sketch_with_dependend_libraries", "sketch_with_dependend_libraries.ino"))
 }
 
 func TestTryBuild021(t *testing.T) {
-	tryBuild(t, "sketch_with_function_pointer", "sketch_with_function_pointer.ino")
+	tryBuild(t, paths.New("sketch_with_function_pointer", "sketch_with_function_pointer.ino"))
 }
 
 func TestTryBuild022(t *testing.T) {
-	ctx := makeDefaultContext(t)
-	ctx.FQBN = parseFQBN(t, "arduino:samd:arduino_zero_native")
-	tryBuildWithContext(t, ctx, "sketch_usbhost", "sketch_usbhost.ino")
+	ctx := makeDefaultContext()
+	tryBuildWithContext(t, ctx, "arduino:samd:arduino_zero_native", paths.New("sketch_usbhost", "sketch_usbhost.ino"))
 }
 
 func TestTryBuild023(t *testing.T) {
-	tryBuild(t, "sketch1", "sketch1.ino")
+	tryBuild(t, paths.New("sketch1", "sketch1.ino"))
 }
 
 func TestTryBuild024(t *testing.T) {
-	tryBuild(t, "SketchWithIfDef", "SketchWithIfDef.ino")
+	tryBuild(t, paths.New("SketchWithIfDef", "SketchWithIfDef.ino"))
 }
 
 // The library for this sketch is missing
 //func TestTryBuild025(t *testing.T) {
-//	tryBuild(t, "sketch3", "Baladuino.ino")
+//	tryBuild(t, paths.New("sketch3", "Baladuino.ino"))
 //}
 
 func TestTryBuild026(t *testing.T) {
-	tryBuild(t, "CharWithEscapedDoubleQuote", "CharWithEscapedDoubleQuote.ino")
+	tryBuild(t, paths.New("CharWithEscapedDoubleQuote", "CharWithEscapedDoubleQuote.ino"))
 }
 
 func TestTryBuild027(t *testing.T) {
-	tryBuild(t, "IncludeBetweenMultilineComment", "IncludeBetweenMultilineComment.ino")
+	tryBuild(t, paths.New("IncludeBetweenMultilineComment", "IncludeBetweenMultilineComment.ino"))
 }
 
 func TestTryBuild028(t *testing.T) {
-	tryBuild(t, "LineContinuations", "LineContinuations.ino")
+	tryBuild(t, paths.New("LineContinuations", "LineContinuations.ino"))
 }
 
 func TestTryBuild029(t *testing.T) {
-	tryBuild(t, "StringWithComment", "StringWithComment.ino")
+	tryBuild(t, paths.New("StringWithComment", "StringWithComment.ino"))
 }
 
 func TestTryBuild030(t *testing.T) {
-	tryBuild(t, "SketchWithStruct", "SketchWithStruct.ino")
+	tryBuild(t, paths.New("SketchWithStruct", "SketchWithStruct.ino"))
 }
 
 func TestTryBuild031(t *testing.T) {
-	tryBuild(t, "sketch9", "sketch9.ino")
+	tryBuild(t, paths.New("sketch9", "sketch9.ino"))
 }
 
 func TestTryBuild032(t *testing.T) {
-	tryBuild(t, "sketch10", "sketch10.ino")
+	tryBuild(t, paths.New("sketch10", "sketch10.ino"))
 }
 
 func TestTryBuild033(t *testing.T) {
-	tryBuild(t, "sketch_that_includes_arduino_h", "sketch_that_includes_arduino_h.ino")
+	tryBuild(t, paths.New("sketch_that_includes_arduino_h", "sketch_that_includes_arduino_h.ino"))
 }
 
 func TestTryBuild034(t *testing.T) {
-	tryBuild(t, "sketch_with_static_asserts", "sketch_with_static_asserts.ino")
+	tryBuild(t, paths.New("sketch_with_static_asserts", "sketch_with_static_asserts.ino"))
 }
 
 func TestTryBuild035(t *testing.T) {
-	tryBuild(t, "sketch_with_enum_class", "sketch_with_enum_class.ino")
+	tryBuild(t, paths.New("sketch_with_enum_class", "sketch_with_enum_class.ino"))
 }
 
 func TestTryBuild036(t *testing.T) {
-	ctx := makeDefaultContext(t)
-	ctx.FQBN = parseFQBN(t, "arduino:samd:arduino_zero_native")
-	tryBuildWithContext(t, ctx, "sketch_fastleds", "sketch_fastleds.ino")
+	ctx := makeDefaultContext()
+	tryBuildWithContext(t, ctx, "arduino:samd:arduino_zero_native", paths.New("sketch_fastleds", "sketch_fastleds.ino"))
 }
 
 func TestTryBuild037(t *testing.T) {
-	tryBuild(t, "sketch_with_externC", "sketch_with_externC.ino")
+	tryBuild(t, paths.New("sketch_with_externC", "sketch_with_externC.ino"))
 }
 
 func TestTryBuild038(t *testing.T) {
-	tryBuild(t, "sketch_with_multiline_prototypes", "sketch_with_multiline_prototypes.ino")
+	tryBuild(t, paths.New("sketch_with_multiline_prototypes", "sketch_with_multiline_prototypes.ino"))
 }
 
 func TestTryBuild039(t *testing.T) {
-	ctx := makeDefaultContext(t)
-	ctx.FQBN = parseFQBN(t, "arduino:samd:arduino_zero_native")
-	tryBuildWithContext(t, ctx, "sketch12", "sketch12.ino")
+	ctx := makeDefaultContext()
+	tryBuildWithContext(t, ctx, "arduino:samd:arduino_zero_native", paths.New("sketch12", "sketch12.ino"))
 }
 
 func TestTryBuild040(t *testing.T) {
-	tryBuild(t, "sketch_with_externC_multiline", "sketch_with_externC_multiline.ino")
+	tryBuild(t, paths.New("sketch_with_externC_multiline", "sketch_with_externC_multiline.ino"))
 }
 
 func TestTryBuild041(t *testing.T) {
-	tryBuild(t, "sketch_with_multiline_template", "sketch_with_multiline_template.ino")
+	tryBuild(t, paths.New("sketch_with_multiline_template", "sketch_with_multiline_template.ino"))
 }
 
 func TestTryBuild042(t *testing.T) {
-	tryBuild(t, "sketch_with_fake_function_pointer", "sketch_with_fake_function_pointer.ino")
+	tryBuild(t, paths.New("sketch_with_fake_function_pointer", "sketch_with_fake_function_pointer.ino"))
 }
 
-func makeDefaultContext(t *testing.T) *types.Context {
-	DownloadCoresAndToolsAndLibraries(t)
-
-	ctx := &types.Context{
+func makeDefaultContext() *types.Context {
+	return &types.Context{
 		HardwareDirs:         paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "downloaded_board_manager_stuff"),
 		BuiltInToolsDirs:     paths.NewPathList("downloaded_tools"),
 		BuiltInLibrariesDirs: paths.New("downloaded_libraries"),
 		OtherLibrariesDirs:   paths.NewPathList("libraries"),
-		FQBN:                 parseFQBN(t, "arduino:avr:leonardo"),
 		Verbose:              true,
 		DebugPreprocessor:    true,
 	}
-	buildPath := SetupBuildPath(t, ctx)
-	defer buildPath.RemoveAll()
-
-	return ctx
 }
 
-func tryBuild(t *testing.T, sketchPath ...string) {
-	ctx := makeDefaultContext(t)
-	tryBuildWithContext(t, ctx, sketchPath...)
+func tryBuild(t *testing.T, sketchLocation *paths.Path) {
+	tryBuildWithContext(t, makeDefaultContext(), "arduino:avr:leonardo", sketchLocation)
 }
 
-func tryBuildWithContext(t *testing.T, ctx *types.Context, sketchPath ...string) {
-	sketchLocation := paths.New(sketchPath...)
-	ctx.Sketch = OpenSketch(t, sketchLocation)
+func tryBuildWithContext(t *testing.T, ctx *types.Context, fqbn string, sketchLocation *paths.Path) {
+	ctx = prepareBuilderTestContext(t, ctx, sketchLocation, fqbn)
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	err := builder.RunBuilder(ctx)
 	NoError(t, err, "Build error for "+sketchLocation.String())
 }
 
-func tryPreprocess(t *testing.T, sketchPath ...string) {
-	ctx := makeDefaultContext(t)
-	tryPreprocessWithContext(t, ctx, sketchPath...)
+func tryPreprocess(t *testing.T, sketchLocation *paths.Path) {
+	tryPreprocessWithContext(t, makeDefaultContext(), "arduino:avr:leonardo", sketchLocation)
 }
 
-func tryPreprocessWithContext(t *testing.T, ctx *types.Context, sketchPath ...string) {
-	sketchLocation := paths.New(sketchPath...)
-	ctx.Sketch = OpenSketch(t, sketchLocation)
+func tryPreprocessWithContext(t *testing.T, ctx *types.Context, fqbn string, sketchLocation *paths.Path) {
+	ctx = prepareBuilderTestContext(t, ctx, sketchLocation, fqbn)
+	defer cleanUpBuilderTestContext(t, ctx)
 
 	err := builder.RunPreprocess(ctx)
 	NoError(t, err, "Build error for "+sketchLocation.String())
diff --git a/legacy/builder/types/context.go b/legacy/builder/types/context.go
index ebf85b19f39..0048d6d0c02 100644
--- a/legacy/builder/types/context.go
+++ b/legacy/builder/types/context.go
@@ -78,15 +78,12 @@ type Context struct {
 	BuildOptionsJson         string
 	BuildOptionsJsonPrevious string
 
-	PackageManager             *packagemanager.Explorer
-	Hardware                   cores.Packages
-	AllTools                   []*cores.ToolRelease
-	RequiredTools              []*cores.ToolRelease
-	TargetBoard                *cores.Board
-	TargetBoardBuildProperties *properties.Map
-	TargetPackage              *cores.Package
-	TargetPlatform             *cores.PlatformRelease
-	ActualPlatform             *cores.PlatformRelease
+	PackageManager *packagemanager.Explorer
+	RequiredTools  []*cores.ToolRelease
+	TargetBoard    *cores.Board
+	TargetPackage  *cores.Package
+	TargetPlatform *cores.PlatformRelease
+	ActualPlatform *cores.PlatformRelease
 
 	BuildProperties              *properties.Map
 	BuildPath                    *paths.Path
@@ -130,16 +127,12 @@ type Context struct {
 	Verbose           bool
 	DebugPreprocessor bool
 
-	// Compile optimization settings
-	OptimizeForDebug  bool
-	OptimizationFlags string
-
 	// Dry run, only create progress map
 	Progress ProgressStruct
 	// Send progress events to this callback
 	ProgressCB rpc.TaskProgressCB
 
-	// Contents of a custom build properties file (line by line)
+	// Custom build properties defined by user (line by line as "key=value" pairs)
 	CustomBuildProperties []string
 
 	// Reuse old tools since the backing storage didn't change
@@ -214,7 +207,7 @@ func (ctx *Context) ExtractBuildOptions() *properties.Map {
 	opts.Set("fqbn", ctx.FQBN.String())
 	opts.Set("customBuildProperties", strings.Join(ctx.CustomBuildProperties, ","))
 	opts.Set("additionalFiles", strings.Join(additionalFilesRelative, ","))
-	opts.Set("compiler.optimization_flags", ctx.OptimizationFlags)
+	opts.Set("compiler.optimization_flags", ctx.BuildProperties.Get("compiler.optimization_flags"))
 	return opts
 }
 
diff --git a/legacy/builder/types/context_test.go b/legacy/builder/types/context_test.go
index 33339cbb9e1..491c49acf6e 100644
--- a/legacy/builder/types/context_test.go
+++ b/legacy/builder/types/context_test.go
@@ -21,6 +21,7 @@ import (
 	"github.com/arduino/arduino-cli/arduino/cores"
 	"github.com/arduino/arduino-cli/arduino/sketch"
 	paths "github.com/arduino/go-paths-helper"
+	"github.com/arduino/go-properties-orderedmap"
 	"github.com/stretchr/testify/require"
 )
 
@@ -37,7 +38,7 @@ func TestInjectBuildOption(t *testing.T) {
 			Sketch:                &sketch.Sketch{FullPath: paths.New("hhh")},
 			FQBN:                  fqbn,
 			CustomBuildProperties: []string{"jjj", "kkk"},
-			OptimizationFlags:     "lll",
+			BuildProperties:       properties.NewFromHashmap(map[string]string{"compiler.optimization_flags": "lll"}),
 		}
 		opts := ctx.ExtractBuildOptions()
 		require.Equal(t, `properties.Map{
diff --git a/rpc/cc/arduino/cli/commands/v1/board.pb.go b/rpc/cc/arduino/cli/commands/v1/board.pb.go
index 0acad885e22..570a3f8ca3b 100644
--- a/rpc/cc/arduino/cli/commands/v1/board.pb.go
+++ b/rpc/cc/arduino/cli/commands/v1/board.pb.go
@@ -127,6 +127,8 @@ type BoardDetailsResponse struct {
 	DebuggingSupported bool `protobuf:"varint,14,opt,name=debugging_supported,json=debuggingSupported,proto3" json:"debugging_supported,omitempty"`
 	// Identifying information for the board (e.g., USB VID/PID).
 	IdentificationProperties []*BoardIdentificationProperties `protobuf:"bytes,15,rep,name=identification_properties,json=identificationProperties,proto3" json:"identification_properties,omitempty"`
+	// Board build properties used for compiling
+	BuildProperties []string `protobuf:"bytes,16,rep,name=build_properties,json=buildProperties,proto3" json:"build_properties,omitempty"`
 }
 
 func (x *BoardDetailsResponse) Reset() {
@@ -259,6 +261,13 @@ func (x *BoardDetailsResponse) GetIdentificationProperties() []*BoardIdentificat
 	return nil
 }
 
+func (x *BoardDetailsResponse) GetBuildProperties() []string {
+	if x != nil {
+		return x.BuildProperties
+	}
+	return nil
+}
+
 type BoardIdentificationProperties struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1460,7 +1469,7 @@ var file_cc_arduino_cli_commands_v1_board_proto_rawDesc = []byte{
 	0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61,
 	0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a,
 	0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62,
-	0x6e, 0x22, 0xef, 0x05, 0x0a, 0x14, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69,
+	0x6e, 0x22, 0x9a, 0x06, 0x0a, 0x14, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69,
 	0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71,
 	0x62, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x12,
 	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
@@ -1507,168 +1516,170 @@ var file_cc_arduino_cli_commands_v1_board_proto_rawDesc = []byte{
 	0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50,
 	0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x18, 0x69, 0x64, 0x65, 0x6e, 0x74,
 	0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74,
-	0x69, 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x1d, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x64, 0x65,
-	0x6e, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65,
-	0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x69, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74,
-	0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x63, 0x63, 0x2e, 0x61,
-	0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
-	0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x64, 0x65, 0x6e,
-	0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72,
-	0x74, 0x69, 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45,
-	0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73,
-	0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e,
-	0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,
-	0xbc, 0x01, 0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6d,
-	0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x0a, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75,
-	0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1f, 0x0a,
-	0x0b, 0x77, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x0a, 0x77, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x14,
-	0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
-	0x6d, 0x61, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x68, 0x65, 0x6c, 0x70,
-	0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75,
-	0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73,
-	0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x6c, 0x70, 0x52, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x22, 0x1e,
-	0x0a, 0x04, 0x48, 0x65, 0x6c, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0xd0,
-	0x01, 0x0a, 0x0d, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d,
-	0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63,
-	0x74, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79,
-	0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
-	0x72, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x5f, 0x66, 0x69,
-	0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x72,
-	0x63, 0x68, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a,
-	0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a,
-	0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a,
-	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x22, 0x9c, 0x01, 0x0a, 0x11, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x44, 0x65, 0x70, 0x65, 0x6e,
-	0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61,
-	0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61,
-	0x67, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
-	0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
-	0x6e, 0x12, 0x3d, 0x0a, 0x07, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e,
-	0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e,
-	0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x07, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73,
-	0x22, 0x8a, 0x01, 0x0a, 0x07, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1a, 0x0a, 0x08,
-	0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
-	0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10,
-	0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65,
-	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x46,
-	0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a,
-	0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x8a, 0x01,
-	0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16,
-	0x0a, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
-	0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-	0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x3f, 0x0a, 0x06, 0x76, 0x61, 0x6c,
-	0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x63, 0x2e, 0x61,
-	0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
-	0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c,
-	0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x60, 0x0a, 0x0b, 0x43, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
-	0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12,
-	0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c,
-	0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01,
-	0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x82, 0x01, 0x0a,
-	0x10, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f,
-	0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31,
-	0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61,
-	0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a,
-	0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62,
-	0x6e, 0x22, 0x53, 0x0a, 0x11, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18,
-	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69,
-	0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e,
-	0x76, 0x31, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52,
-	0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x65, 0x63,
-	0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x52, 0x0a, 0x0f, 0x6d, 0x61, 0x74, 0x63, 0x68,
-	0x69, 0x6e, 0x67, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c,
-	0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f,
-	0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0e, 0x6d, 0x61, 0x74,
-	0x63, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x12, 0x34, 0x0a, 0x04, 0x70,
-	0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61,
-	0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
-	0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72,
-	0x74, 0x22, 0xac, 0x01, 0x0a, 0x13, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x41,
-	0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73,
-	0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63,
+	0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x6f,
+	0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc9,
+	0x01, 0x0a, 0x1d, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+	0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73,
+	0x12, 0x69, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e,
+	0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76,
+	0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x63,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e,
+	0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
+	0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50,
+	0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
+	0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
+	0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbc, 0x01, 0x0a, 0x07, 0x50,
+	0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61,
+	0x69, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x69, 0x6e,
+	0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x65, 0x62, 0x73,
+	0x69, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77,
+	0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61,
+	0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12,
+	0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63,
+	0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48,
+	0x65, 0x6c, 0x70, 0x52, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x22, 0x1e, 0x0a, 0x04, 0x48, 0x65, 0x6c,
+	0x70, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x06, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x0d, 0x42, 0x6f,
+	0x61, 0x72, 0x64, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x61,
+	0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12,
+	0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75,
+	0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x29, 0x0a,
+	0x10, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65,
+	0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63,
+	0x6b, 0x73, 0x75, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x65, 0x63,
+	0x6b, 0x73, 0x75, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01,
+	0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x9c, 0x01, 0x0a,
+	0x11, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69,
+	0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x72, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x72, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x07,
+	0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e,
+	0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63,
+	0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65,
+	0x6d, 0x73, 0x52, 0x07, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x8a, 0x01, 0x0a, 0x07,
+	0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b,
+	0x73, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b,
+	0x73, 0x75, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x72, 0x63, 0x68, 0x69,
+	0x76, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01,
+	0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x8a, 0x01, 0x0a, 0x0c, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x62, 0x65,
+	0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4c,
+	0x61, 0x62, 0x65, 0x6c, 0x12, 0x3f, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e,
+	0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76,
+	0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x60, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56,
+	0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x73,
+	0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73,
+	0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x82, 0x01, 0x0a, 0x10, 0x42, 0x6f, 0x61, 0x72,
+	0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08,
+	0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24,
+	0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e,
+	0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74,
+	0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x18,
+	0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
+	0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x22, 0x53, 0x0a, 0x11,
+	0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x12, 0x3e, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c,
+	0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65,
+	0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74,
+	0x73, 0x22, 0x98, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f,
+	0x72, 0x74, 0x12, 0x52, 0x0a, 0x0f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x62,
+	0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63,
 	0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d,
-	0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
-	0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73,
-	0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
-	0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x72, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x15,
-	0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x5f, 0x62,
-	0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63,
-	0x6c, 0x75, 0x64, 0x65, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x73,
-	0x22, 0x59, 0x0a, 0x14, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x62, 0x6f, 0x61, 0x72,
-	0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72,
-	0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
-	0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49,
-	0x74, 0x65, 0x6d, 0x52, 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x22, 0x77, 0x0a, 0x15, 0x42,
-	0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71,
+	0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69,
+	0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67,
+	0x42, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e,
+	0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76,
+	0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0xac, 0x01, 0x0a,
+	0x13, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71,
 	0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
 	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75,
 	0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73,
 	0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e,
-	0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72,
-	0x75, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72,
-	0x72, 0x75, 0x70, 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x16, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69,
-	0x73, 0x74, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
-	0x1d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c,
-	0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63,
-	0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f,
-	0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74,
-	0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05,
-	0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72,
-	0x6f, 0x72, 0x22, 0x96, 0x01, 0x0a, 0x0d, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74,
-	0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1b, 0x0a, 0x09,
-	0x69, 0x73, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
-	0x08, 0x69, 0x73, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x12, 0x40, 0x0a, 0x08, 0x70, 0x6c, 0x61,
-	0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63,
+	0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
+	0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61,
+	0x72, 0x63, 0x68, 0x41, 0x72, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75,
+	0x64, 0x65, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x48,
+	0x69, 0x64, 0x64, 0x65, 0x6e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x22, 0x59, 0x0a, 0x14, 0x42,
+	0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f,
+	0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31,
+	0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06,
+	0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x22, 0x77, 0x0a, 0x15, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c,
+	0x69, 0x73, 0x74, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63,
+	0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49,
+	0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
+	0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x22,
+	0x8b, 0x01, 0x0a, 0x16, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x74,
+	0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76,
+	0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+	0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x70, 0x6f, 0x72,
+	0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64,
+	0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
+	0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72,
+	0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x96, 0x01,
+	0x0a, 0x0d, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12,
+	0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x68, 0x69,
+	0x64, 0x64, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x48, 0x69,
+	0x64, 0x64, 0x65, 0x6e, 0x12, 0x40, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d,
+	0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75,
+	0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73,
+	0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x08, 0x70, 0x6c,
+	0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0xab, 0x01, 0x0a, 0x12, 0x42, 0x6f, 0x61, 0x72, 0x64,
+	0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a,
+	0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69,
+	0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73,
+	0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12,
+	0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x72, 0x67, 0x73,
+	0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x68, 0x69, 0x64, 0x64,
+	0x65, 0x6e, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x42, 0x6f,
+	0x61, 0x72, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x13, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x53, 0x65, 0x61,
+	0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x62,
+	0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63,
 	0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d,
-	0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72,
-	0x6d, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0xab, 0x01, 0x0a, 0x12,
-	0x42, 0x6f, 0x61, 0x72, 0x64, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e,
-	0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76,
-	0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74,
-	0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x61,
-	0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, 0x63,
-	0x68, 0x41, 0x72, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65,
-	0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03,
-	0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x48, 0x69, 0x64,
-	0x64, 0x65, 0x6e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x13, 0x42, 0x6f, 0x61,
-	0x72, 0x64, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x12, 0x41, 0x0a, 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c,
-	0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f,
-	0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x62, 0x6f, 0x61,
-	0x72, 0x64, 0x73, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
-	0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e,
-	0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64,
-	0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
-	0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69,
+	0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x42, 0x48,
+	0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64,
+	0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69,
+	0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f,
+	0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b,
+	0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/rpc/cc/arduino/cli/commands/v1/board.proto b/rpc/cc/arduino/cli/commands/v1/board.proto
index 53f9d571334..329c2ce5d22 100644
--- a/rpc/cc/arduino/cli/commands/v1/board.proto
+++ b/rpc/cc/arduino/cli/commands/v1/board.proto
@@ -60,6 +60,8 @@ message BoardDetailsResponse {
   bool debugging_supported = 14;
   // Identifying information for the board (e.g., USB VID/PID).
   repeated BoardIdentificationProperties identification_properties = 15;
+  // Board build properties used for compiling
+  repeated string build_properties = 16;
 }
 
 message BoardIdentificationProperties {