-
Notifications
You must be signed in to change notification settings - Fork 92
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
Add particle flash --tachyon
#780
base: master
Are you sure you want to change the base?
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -12,8 +12,9 @@ const CloudCommand = require('./cloud'); | |||||
const BundleCommand = require('./bundle'); | ||||||
const temp = require('temp').track(); | ||||||
const { knownAppNames, knownAppsForPlatform } = require('../lib/known-apps'); | ||||||
const { sourcePatterns, binaryPatterns, binaryExtensions } = require('../lib/file-types'); | ||||||
const { sourcePatterns, binaryPatterns, binaryExtensions, linuxExecPatterns } = require('../lib/file-types'); | ||||||
const deviceOsUtils = require('../lib/device-os-version-util'); | ||||||
const os = require('os'); | ||||||
const semver = require('semver'); | ||||||
const { | ||||||
createFlashSteps, | ||||||
|
@@ -25,6 +26,7 @@ const { | |||||
} = require('../lib/flash-helper'); | ||||||
const createApiCache = require('../lib/api-cache'); | ||||||
const { validateDFUSupport } = require('./device-util'); | ||||||
const execa = require('execa'); | ||||||
|
||||||
module.exports = class FlashCommand extends CLICommandBase { | ||||||
constructor(...args) { | ||||||
|
@@ -39,9 +41,11 @@ module.exports = class FlashCommand extends CLICommandBase { | |||||
target, | ||||||
port, | ||||||
yes, | ||||||
verbose, | ||||||
tachyon, | ||||||
'application-only': applicationOnly | ||||||
}) { | ||||||
if (!device && !binary && !local) { | ||||||
if (!tachyon && !device && !binary && !local) { | ||||||
// if no device nor files are passed, show help | ||||||
throw usageError('You must specify a device or a file'); | ||||||
} | ||||||
|
@@ -55,11 +59,109 @@ module.exports = class FlashCommand extends CLICommandBase { | |||||
} else if (local) { | ||||||
let allFiles = binary ? [binary, ...files] : files; | ||||||
await this.flashLocal({ files: allFiles, applicationOnly, target }); | ||||||
} else if (tachyon) { | ||||||
let allFiles = binary ? [binary, ...files] : files; | ||||||
await this.flashTachyon({ verbose, files: allFiles }); | ||||||
} else { | ||||||
await this.flashCloud({ device, files, target }); | ||||||
} | ||||||
} | ||||||
|
||||||
async flashTachyon({ verbose, files }) { | ||||||
this.ui.write(`Ensure only one device is connected to a computer${os.EOL}`); | ||||||
|
||||||
let unpackToolFolder; | ||||||
if (files.length === 0) { | ||||||
// If no files are passed, assume the current directory | ||||||
unpackToolFolder = process.cwd(); | ||||||
files = await fs.readdir(unpackToolFolder); | ||||||
} else if (files.length === 1) { | ||||||
// If only one file is passed, check if it's a directory | ||||||
const stats = await fs.stat(files[0]); | ||||||
if (stats.isDirectory()) { | ||||||
unpackToolFolder = files[0]; | ||||||
files = await fs.readdir(files[0]); | ||||||
} | ||||||
} else { | ||||||
// If multiple files are passed, check the directory from the first file | ||||||
unpackToolFolder = path.dirname(files[0]); | ||||||
} | ||||||
|
||||||
const parsedFiles = await this._analyzeFiles(files); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at the implementation of |
||||||
let linxuFiles = await this._findLinuxExecutableFiles(parsedFiles.files, { directory: unpackToolFolder }); | ||||||
linxuFiles = linxuFiles.map(f => path.basename(f)); | ||||||
|
||||||
|
||||||
const elfFiles = linxuFiles.filter(f => f.startsWith('prog') && f.endsWith('.elf')); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change the criteria to
Suggested change
|
||||||
const rawProgramFiles = linxuFiles.filter(f => f.startsWith('rawprogram') && f.endsWith('.xml')); | ||||||
const patchFiles = linxuFiles.filter(f => f.startsWith('patch') && f.endsWith('.xml')); | ||||||
|
||||||
if (!elfFiles.length || !rawProgramFiles.length || !patchFiles.length) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think patch files are mandatory. |
||||||
throw new Error('The directory should contain at least one .elf file, one rawprogram file, and one patch file'); | ||||||
} | ||||||
|
||||||
const sortByNumber = (a, b) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's necessary to sort the files, as long as the program files are before the patch files. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you want to process in the order given in the command. The original tools assume that the user correctly orders the raw program and patch files accordingly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I brought these back (for time being) and kept the below changes as well (assuming they are important too) |
||||||
const extractNumber = str => parseInt(str.match(/(\d+).xml/)[1]); | ||||||
return extractNumber(a) - extractNumber(b); | ||||||
}; | ||||||
|
||||||
rawProgramFiles.sort(sortByNumber); | ||||||
patchFiles.sort(sortByNumber); | ||||||
|
||||||
if (rawProgramFiles.length !== patchFiles.length) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is necessary either There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really think this is extra. What if a program doesn't have a patch file to go with it? |
||||||
throw new Error('The number of rawprogram files should match the number of patch files'); | ||||||
} | ||||||
|
||||||
let filesToProgram = []; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// interleave the rawprogram files and patch files | ||||||
for (let i = 0; i < rawProgramFiles.length; i++) { | ||||||
filesToProgram.push(rawProgramFiles[i]); | ||||||
filesToProgram.push(patchFiles[i]); | ||||||
} | ||||||
|
||||||
filesToProgram.unshift(elfFiles[0]); | ||||||
|
||||||
this.ui.write(`Found the following files in the directory:${os.EOL}`); | ||||||
this.ui.write('Loader file:'); | ||||||
this.ui.write(` - ${elfFiles[0]}${os.EOL}`); | ||||||
|
||||||
this.ui.write('Program files:'); | ||||||
for (const file of rawProgramFiles) { | ||||||
this.ui.write(` - ${file}`); | ||||||
} | ||||||
this.ui.write(os.EOL); | ||||||
|
||||||
this.ui.write('Patch files:'); | ||||||
for (const file of patchFiles) { | ||||||
this.ui.write(` - ${file}`); | ||||||
} | ||||||
this.ui.write(os.EOL); | ||||||
|
||||||
const qdl = path.join(__dirname, '../../assets/qdl/qdl'); | ||||||
await fs.ensureFile(qdl); | ||||||
|
||||||
this.ui.write(`Command: ${qdl} --storage ufs ${filesToProgram.join(' ')}${os.EOL}`); | ||||||
this.ui.write(`Starting download. The download may take several minutes${os.EOL}`); | ||||||
|
||||||
try { | ||||||
const res = await execa(qdl, ['--storage', 'ufs', ...filesToProgram], { | ||||||
cwd: unpackToolFolder, | ||||||
stdio: verbose ? 'inherit' : 'pipe' | ||||||
}); | ||||||
// put the output in a log file if not verbose | ||||||
if (!verbose) { | ||||||
const outputLog = path.join(process.cwd(), `qdl-output-${Date.now()}.log`); | ||||||
await fs.writeFile(outputLog, res.stdout); | ||||||
this.ui.write(`Download complete. Output log available at ${outputLog}${os.EOL}`); | ||||||
} else { | ||||||
this.ui.write(`Download complete${os.EOL}`); | ||||||
} | ||||||
} catch (err) { | ||||||
throw new Error(`Download failed. Try powering up your board in EDL mode and try again.${os.EOL}Error: ${err.message}${os.EOL}`); | ||||||
} | ||||||
// TODO: Handle errors | ||||||
} | ||||||
|
||||||
async flashOverUsb({ binary, factory }) { | ||||||
if (utilities.getFilenameExt(binary) === '.zip') { | ||||||
throw new Error("Use 'particle flash --local' to flash a zipped bundle."); | ||||||
|
@@ -293,24 +395,37 @@ module.exports = class FlashCommand extends CLICommandBase { | |||||
} | ||||||
|
||||||
async _findBinaries(parsedFiles) { | ||||||
const binaries = new Set(); | ||||||
for (const filePath of parsedFiles) { | ||||||
return this._findFiles(parsedFiles, binaryPatterns); | ||||||
} | ||||||
|
||||||
async _findLinuxExecutableFiles(parsedFiles, { directory }) { | ||||||
|
||||||
if (directory) { | ||||||
const files = parsedFiles.map(f => path.join(directory, f)); | ||||||
return this._findFiles(files, linuxExecPatterns); | ||||||
} | ||||||
return this._findFiles(parsedFiles, linuxExecPatterns); | ||||||
} | ||||||
|
||||||
async _findFiles(files, patterns) { | ||||||
const resFiles = new Set(); | ||||||
for (const filePath of files) { | ||||||
try { | ||||||
const stats = await fs.stat(filePath); | ||||||
if (stats.isDirectory()) { | ||||||
const found = utilities.globList(filePath, binaryPatterns); | ||||||
for (const binary of found) { | ||||||
binaries.add(binary); | ||||||
const found = utilities.globList(filePath, patterns); | ||||||
for (const file of found) { | ||||||
resFiles.add(file); | ||||||
} | ||||||
} else { | ||||||
binaries.add(filePath); | ||||||
resFiles.add(filePath); | ||||||
} | ||||||
} catch (error) { | ||||||
throw new Error(`I couldn't find that: ${filePath}`); | ||||||
} | ||||||
|
||||||
} | ||||||
return Array.from(binaries); | ||||||
return Array.from(resFiles); | ||||||
} | ||||||
|
||||||
async _processBundle({ filesToFlash }) { | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to add
verbose
here. It's a global flag available to all commands already https://github.com/particle-iot/particle-cli/blob/master/src/app/cli.js#L42