Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ability to find required ncs paths #44

Merged
merged 2 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions cli/config/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"fmt"
"log"
"os"
"slices"
"strings"
Expand All @@ -23,6 +24,7 @@ type Device struct {

type General struct {
NCSToolChainBase string `yaml:"ncs_toolchain_base"`
NCSVersion string `yaml:"ncs_version"`
ZephyrBase string `yaml:"zephyr_base"`

Manufacturer string
Expand Down Expand Up @@ -50,12 +52,9 @@ type Board struct {
func ParseFromFile(configPath string) (*Device, error) {
cfg := &Device{
General: General{
RunEvery: time.Minute,
// Path for v2.5.0
// TODO: there is nice config file in ~/ncs/toolchains/toolchains.json
// which can be used to determine location of necessary toolchain.
NCSToolChainBase: "~/ncs/toolchains/7795df4459/",
ZephyrBase: "~/ncs/zephyr/",
RunEvery: time.Minute,
NCSToolChainBase: "~/ncs",
NCSVersion: "v2.5.0",
},
}

Expand Down Expand Up @@ -100,14 +99,30 @@ func (d *Device) PrependCommonClusters() {
func (g General) GetToochainsPath() (string, string) {
// If env variables are defined - they have higher priority.
ncsToolchainPath := os.Getenv("NCS_TOOLCHAIN_BASE")
ncsVersion := os.Getenv("NCS_VERSION")
zephyrPath := os.Getenv("ZEPHYR_BASE")

if ncsVersion == "" {
ncsVersion = g.NCSVersion
}

var locations NCSLocation
if ncsToolchainPath == "" || zephyrPath == "" {
var err error
locations, err = FindNCSLocation(g.NCSToolChainBase, ncsVersion)
if err != nil {
log.Panicf("find ncs location: %s", err.Error())
}

log.Printf("found toolchain version %q, requested version %q", locations.Version, ncsVersion)
}

if ncsToolchainPath == "" {
ncsToolchainPath = g.NCSToolChainBase
ncsToolchainPath = locations.NCS
}

if zephyrPath == "" {
zephyrPath = g.ZephyrBase
zephyrPath = locations.Zephyr
}

return ncsToolchainPath, zephyrPath
Expand Down
114 changes: 114 additions & 0 deletions cli/config/ncs_finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package config

import (
"encoding/json"
"fmt"
"log"
"os"
"path"
"slices"

"golang.org/x/exp/maps"
)

type NCSLocation struct {
Version string
NCS string
Zephyr string
}

type toolchainItem struct {
Identifier struct {
BundleID string `json:"bundle_id"`
} `json:"identifier"`
NCSVersions []string `json:"ncs_versions"`
}

type toolchainTopLevelItem struct {
DefaultToolchain map[string]string `json:"default_toolchain"`
Toolchains []toolchainItem `json:"toolchains"`
}

// FindNCSLocation will return paths for NCS and Zephyr toolchains.
//
// If toolchain of required version was not found - it will try to
// use default version from toolchain file, and if it is not present -
// latest available in list of toolchains.
//
// As such this function can return different toolchain version that
// was requested, and caller can check it by comparing to version
// returned in NCSLocation.
func FindNCSLocation(ncsBase, version string) (NCSLocation, error) {
toolchainsJson := toolchainConfigPath(ncsBase)

configFile, err := os.Open(toolchainsJson)
if err != nil {
return NCSLocation{}, fmt.Errorf("open toolchains.json file at %q: %w", toolchainsJson, err)
}

var toolchainFile []toolchainTopLevelItem

err = json.NewDecoder(configFile).Decode(&toolchainFile)
if err != nil {
return NCSLocation{}, fmt.Errorf("decode toolchain file: %w", err)
}

if len(toolchainFile) == 0 {
return NCSLocation{}, fmt.Errorf("toolchain file does not contain definitions")
}

first := toolchainFile[0]

return providePaths(ncsBase, version, first)
}

func providePaths(ncsBase, version string, toolchainItem toolchainTopLevelItem) (NCSLocation, error) {
versionToIdentifier := mapVersions(toolchainItem)

availableVersions := maps.Keys(versionToIdentifier)
if len(availableVersions) == 0 {
return NCSLocation{}, fmt.Errorf("no toolchain versions found in toolchain configuration path %q", toolchainConfigPath(ncsBase))
}

log.Printf("available toolchain versions: %v", availableVersions)

bundleID := versionToIdentifier[version]

if bundleID == "" {
version = toolchainItem.DefaultToolchain["ncs_version"]
bundleID = versionToIdentifier[version]
}

if bundleID == "" {
log.Println("toolchain config does not provide default version, and required version was not found, falling back to latest version in config")

slices.Sort(availableVersions)
version = availableVersions[len(availableVersions)-1]

bundleID = versionToIdentifier[version]
}

return constructPaths(ncsBase, version, bundleID), nil
}

func mapVersions(toolchainItem toolchainTopLevelItem) map[string]string {
mapped := make(map[string]string, len(toolchainItem.Toolchains))

for _, toolchain := range toolchainItem.Toolchains {
mapped[toolchain.NCSVersions[0]] = toolchain.Identifier.BundleID
}

return mapped
}

func constructPaths(ncsBase, version, bundleID string) NCSLocation {
return NCSLocation{
Version: version,
NCS: path.Join(ncsBase, "toolchains", bundleID),
Zephyr: path.Join(ncsBase, version, "zephyr"),
}
}

func toolchainConfigPath(ncsBase string) string {
return path.Join(ncsBase, "toolchains", "toolchains.json")
}
24 changes: 15 additions & 9 deletions cli/runner/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package runner
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"path"
Expand Down Expand Up @@ -77,7 +78,13 @@ func WithWorkDir(workDir string) CmdOpt {
// WithToolchainPath updates environment of command
// to inlcude necessary variables for building firmware.
func WithToolchainPath(ncsToolchainBase, zephyrBase string) CmdOpt {
if ncsToolchainBase == "" || zephyrBase == "" {
// For now check that we don't want to setup env here,
// and move it to CLI ASAP.
// This could be useful if run inside environment that
// is already set up properly.
if noSetupEnv() || ncsToolchainBase == "" || zephyrBase == "" {
log.Println("environment will not be prepared because either one of the paths is empty, or requested not to")

return func(c *exec.Cmd) {}
}

Expand Down Expand Up @@ -107,14 +114,9 @@ func extendEnv(ncsToolchainPath string, zephyrPath string) []string {
envPath := os.Getenv("PATH")
combinedPath := ncsCombinedPath
if envPath != "" {
combinedPath += ":" + envPath
combinedPath += string(os.PathListSeparator) + envPath
}

pythonPath := generateEnvArray(ncsToolchainPath, []string{
"/usr/local/lib/python3.8",
"/usr/local/lib/python3.8/site-packages",
})

ldLibraryPath := generateEnvArray(ncsToolchainPath, []string{
"/usr/lib",
"/usr/lib/x86_64-linux-gnu",
Expand All @@ -126,8 +128,6 @@ func extendEnv(ncsToolchainPath string, zephyrPath string) []string {
"ZEPHYR_BASE=" + zephyrPath,
"ZEPHYR_SDK_INSTALL_DIR=" + path.Join(ncsToolchainPath, "/opt/zephyr-sdk"),
"ZEPHYR_TOOLCHAIN_VARIANT=zephyr",
"PYTHOHOME=" + path.Join(ncsCombinedPath, "/usr/local"),
"PYTHONPATH=" + pythonPath,
"LD_LIBRARY_PATH=" + ldLibraryPath,
}
}
Expand Down Expand Up @@ -160,3 +160,9 @@ func updateCurrentPath(envs []string) {
os.Setenv("PATH", envPath)
}
}

func noSetupEnv() bool {
_, ok := os.LookupEnv("NO_SETUP_ENV")

return ok
}
9 changes: 4 additions & 5 deletions cli/zigbee.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,10 @@ board:
rx: 1.10
leds:
- id: led_green
# The `pin` section is optional, if led is already present in board definition.
pin:
port: 0
pin: 6
inverted: true
# The pin definition is optional, if led is already present in board definition.
port: 0
pin: 6
inverted: true

sensors:
# - type: bme680
Expand Down