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

Add the force recompile flag #359

Merged
merged 5 commits into from
Jun 4, 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
3 changes: 2 additions & 1 deletion gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ You should have received a copy of the GNU Lesser General Public License
along with the library. If not, see <http://www.gnu.org/licenses/>.
*/

import { codegen, Project } from '@alephium/cli'
import { codegen } from '@alephium/cli'
import { Project } from './packages/cli/src/project'
import { web3 } from '@alephium/web3'

async function gen() {
Expand Down
9 changes: 6 additions & 3 deletions packages/cli/cli_internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@
codegen,
getConfigFile,
getSdkFullNodeVersion,
isDeployedOnMainnet,
isDeployed,
isNetworkLive,
loadConfig
} from './src'
import { Project } from './src/project'

function getConfig(options: any): Configuration {

Check warning on line 37 in packages/cli/cli_internal.ts

View workflow job for this annotation

GitHub Actions / build (16)

Unexpected any. Specify a different type

Check warning on line 37 in packages/cli/cli_internal.ts

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected any. Specify a different type
const configFile = options.config ? (options.config as string) : getConfigFile()
console.log(`Loading alephium config file: ${configFile}`)
const config = loadConfig(configFile)
Expand Down Expand Up @@ -91,7 +91,7 @@
try {
const config = getConfig(options)
const networkId = checkAndGetNetworkId(options.network)
const nodeUrl = config.networks[networkId].nodeUrl

Check warning on line 94 in packages/cli/cli_internal.ts

View workflow job for this annotation

GitHub Actions / build (16)

Generic Object Injection Sink

Check warning on line 94 in packages/cli/cli_internal.ts

View workflow job for this annotation

GitHub Actions / build (18)

Generic Object Injection Sink
if (!(await isNetworkLive(nodeUrl))) {
throw new Error(`${networkId} is not live`)
}
Expand All @@ -103,14 +103,17 @@
console.log(`Full node version: ${connectedFullNodeVersion}`)

const cwd = path.resolve(process.cwd())
const skipSaveArtifacts = config.skipSaveArtifacts || isDeployedOnMainnet(config)
const isContractDeployed = isDeployed(config)
if (!config.forceRecompile && isContractDeployed) {
console.warn(`The contracts has been deployed on testnet/mainnet, and the artifacts will not be updated.`)
}
const project = await Project.compile(
config.compilerOptions,
cwd,
config.sourceDir,
config.artifactDir,
connectedFullNodeVersion,
skipSaveArtifacts
config.forceRecompile || !isContractDeployed
)
console.log('✅ Compilation completed!')
if (options.skipGenerate) {
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
getConfigFile,
getDeploymentFilePath,
getNetwork,
isDeployedOnMainnet,
isDeployed,
loadConfig,
retryFetch,
taskIdToVariable,
Expand Down Expand Up @@ -174,7 +174,7 @@
}

getInstance<I extends ContractInstance>(
contract: ContractFactory<I, any>,

Check warning on line 177 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (16)

Unexpected any. Specify a different type

Check warning on line 177 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected any. Specify a different type
group?: number,
taskId?: string
): I | undefined {
Expand Down Expand Up @@ -225,7 +225,7 @@
return this.contracts.size === 0 && this.scripts.size === 0 && this.migrations.size === 0
}

marshal(): any {

Check warning on line 228 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (16)

Unexpected any. Specify a different type

Check warning on line 228 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected any. Specify a different type
return {
deployerAddress: this.deployerAddress,
contracts: Object.fromEntries(this.contracts),
Expand All @@ -234,7 +234,7 @@
}
}

static unmarshal(json: any): DeploymentsPerAddress {

Check warning on line 237 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (16)

Unexpected any. Specify a different type

Check warning on line 237 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected any. Specify a different type
const deployerAddress = json.deployerAddress as string
const contracts = new Map(Object.entries<DeployContractExecutionResult>(json.contracts))
const scripts = new Map(Object.entries<RunScriptResult>(json.scripts))
Expand Down Expand Up @@ -295,7 +295,7 @@
return true
}
// previous !== undefined if retry is false
return previous!.issueTokenAmount !== issueTokenAmount

Check warning on line 298 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (16)

Forbidden non-null assertion

Check warning on line 298 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (18)

Forbidden non-null assertion
}

async function needToRunScript(
Expand Down Expand Up @@ -575,14 +575,14 @@
const artifactDir = configuration.artifactDir ?? DEFAULT_CONFIGURATION_VALUES.artifactDir
let project: Project | undefined = undefined
if (configuration.skipRecompile !== true) {
const skipSaveArtifacts = configuration.skipSaveArtifacts || isDeployedOnMainnet(configuration)
const forceRecompile = configuration.forceRecompile || !isDeployed(configuration)
project = await Project.compile(
configuration.compilerOptions,
path.resolve(process.cwd()),
configuration.sourceDir ?? DEFAULT_CONFIGURATION_VALUES.sourceDir,
artifactDir,
undefined,
skipSaveArtifacts
forceRecompile
)
}

Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,3 @@ export * from './types'
export * from './utils'
export * from './deployment'
export * from './codegen'
export * from './project'
65 changes: 65 additions & 0 deletions packages/cli/src/project.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
Copyright 2018 - 2022 The Alephium Authors
This file is part of the alephium project.

The library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

The library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with the library. If not, see <http://www.gnu.org/licenses/>.
*/

import { DEFAULT_NODE_COMPILER_OPTIONS } from '@alephium/web3'
import { CodeInfo, ProjectArtifact, SourceInfo } from './project'

describe('project', () => {
function newCodeInfo(hash: string): CodeInfo {
return {
sourceFile: '',
sourceCodeHash: hash,
bytecodeDebugPatch: '',
codeHashDebug: '',
warnings: []
}
}

function newSourceInfo(name: string, hash: string): SourceInfo {
return new SourceInfo(0, name, '', hash, '', false)
}

it('should get changed sources', () => {
const codes: Map<string, CodeInfo> = new Map([
['Foo', newCodeInfo('Foo')],
['Bar', newCodeInfo('Bar')],
['Baz', newCodeInfo('Baz')]
])
const preArtifacts = new ProjectArtifact('', DEFAULT_NODE_COMPILER_OPTIONS, codes)
const sources = [newSourceInfo('Foo', 'Foo1'), newSourceInfo('Bar', 'Bar1'), newSourceInfo('Baz', 'Baz')]
expect(preArtifacts.getChangedSources(sources)).toEqual(['Foo', 'Bar'])
})

it('should get new added sources', () => {
const codes: Map<string, CodeInfo> = new Map([['Foo', newCodeInfo('Foo')]])
const preArtifacts = new ProjectArtifact('', DEFAULT_NODE_COMPILER_OPTIONS, codes)
const sources = [newSourceInfo('Foo', 'Foo'), newSourceInfo('Bar', 'Bar'), newSourceInfo('Baz', 'Baz')]
expect(preArtifacts.getChangedSources(sources)).toEqual(['Bar', 'Baz'])
})

it('should get removed sources', () => {
const codes: Map<string, CodeInfo> = new Map([
['Foo', newCodeInfo('Foo')],
['Bar', newCodeInfo('Bar')],
['Baz', newCodeInfo('Baz')]
])
const preArtifacts = new ProjectArtifact('', DEFAULT_NODE_COMPILER_OPTIONS, codes)
const sources = [newSourceInfo('Bar', 'Bar')]
expect(preArtifacts.getChangedSources(sources)).toEqual(['Foo', 'Baz'])
})
})
84 changes: 51 additions & 33 deletions packages/cli/src/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ enum SourceKind {
Struct = 4
}

class SourceInfo {
export class SourceInfo {
type: SourceKind
name: string
contractRelativePath: string
Expand Down Expand Up @@ -127,7 +127,7 @@ class Compiled<T extends Artifact> {
}
}

type CodeInfo = {
export type CodeInfo = {
sourceFile: string
sourceCodeHash: string
bytecodeDebugPatch: string
Expand Down Expand Up @@ -206,14 +206,21 @@ export class ProjectArtifact {
return fsPromises.writeFile(filepath, content)
}

getChangedSources(sourceInfos: SourceInfo[]): SourceInfo[] {
const result: SourceInfo[] = []
for (const sourceInfo of sourceInfos) {
getChangedSources(sourceInfos: SourceInfo[]): string[] {
const result: string[] = []
// get all changed and new sources
sourceInfos.forEach((sourceInfo) => {
const info = this.infos.get(sourceInfo.name)
if (typeof info === 'undefined' || info.sourceCodeHash !== sourceInfo.sourceCodeHash) {
result.push(sourceInfo)
if (info === undefined || info.sourceCodeHash !== sourceInfo.sourceCodeHash) {
result.push(sourceInfo.name)
}
}
})
// get all removed sources
this.infos.forEach((_, name) => {
if (sourceInfos.find((s) => s.name === name) === undefined) {
result.push(name)
}
})
return result
}

Expand Down Expand Up @@ -364,17 +371,21 @@ export class Project {
static checkCompilerWarnings(
contracts: Map<string, Compiled<Contract>>,
scripts: Map<string, Compiled<Script>>,
changedSources: SourceInfo[],
skipSaveArtifacts: boolean,
changedSources: string[],
forceRecompile: boolean,
errorOnWarnings: boolean
): void {
const warnings: string[] = []
contracts.forEach((contract) => {
if (!skipSaveArtifacts || changedSources.find((s) => s.name === contract.sourceInfo.name) !== undefined) {
if (Project.needToUpdate(forceRecompile, changedSources, contract.sourceInfo.name)) {
warnings.push(...contract.warnings)
}
})
scripts.forEach((script) => warnings.push(...script.warnings))
scripts.forEach((script) => {
if (Project.needToUpdate(forceRecompile, changedSources, script.sourceInfo.name)) {
warnings.push(...script.warnings)
}
})
if (warnings.length !== 0) {
const prefixPerWarning = ' - '
const warningString = prefixPerWarning + warnings.join('\n' + prefixPerWarning)
Expand Down Expand Up @@ -421,10 +432,14 @@ export class Project {
return fsPromises.writeFile(filePath, JSON.stringify(structs, null, 2))
}

private static needToUpdate(forceRecompile: boolean, changedSources: string[], name: string): boolean {
return forceRecompile || changedSources.includes(name)
}

private async saveArtifactsToFile(
projectRootDir: string,
skipSaveArtifacts: boolean,
changedSources: SourceInfo[]
forceRecompile: boolean,
changedSources: string[]
): Promise<void> {
const artifactsRootDir = this.artifactsRootDir
const saveToFile = async function (compiled: Compiled<Artifact>): Promise<void> {
Expand All @@ -436,24 +451,26 @@ export class Project {
return fsPromises.writeFile(artifactPath, compiled.artifact.toString())
}
for (const [_, contract] of this.contracts) {
if (!skipSaveArtifacts || changedSources.find((s) => s.name === contract.sourceInfo.name) !== undefined) {
if (Project.needToUpdate(forceRecompile, changedSources, contract.sourceInfo.name)) {
await saveToFile(contract)
}
}
for (const [_, script] of this.scripts) {
await saveToFile(script)
if (Project.needToUpdate(forceRecompile, changedSources, script.sourceInfo.name)) {
await saveToFile(script)
}
}
await this.saveStructsToFile()
await this.saveProjectArtifact(projectRootDir, skipSaveArtifacts, changedSources)
await this.saveProjectArtifact(projectRootDir, forceRecompile, changedSources)
}

private async saveProjectArtifact(projectRootDir: string, skipSaveArtifacts: boolean, changedSources: SourceInfo[]) {
if (skipSaveArtifacts) {
// we should not update the `codeHashDebug` if the `skipSaveArtifacts` is enabled
private async saveProjectArtifact(projectRootDir: string, forceRecompile: boolean, changedSources: string[]) {
if (!forceRecompile) {
// we should not update the `codeHashDebug` if the `forceRecompile` is disable
const prevProjectArtifact = await ProjectArtifact.from(projectRootDir)
if (prevProjectArtifact !== undefined) {
for (const [name, info] of this.projectArtifact.infos) {
if (changedSources.find((s) => s.name === name) === undefined) {
if (!changedSources.includes(name)) {
const prevInfo = prevProjectArtifact.infos.get(name)
info.bytecodeDebugPatch = prevInfo?.bytecodeDebugPatch ?? info.bytecodeDebugPatch
info.codeHashDebug = prevInfo?.codeHashDebug ?? info.codeHashDebug
Expand Down Expand Up @@ -515,8 +532,8 @@ export class Project {
artifactsRootDir: string,
errorOnWarnings: boolean,
compilerOptions: node.CompilerOptions,
changedSources: SourceInfo[],
skipSaveArtifacts = false
changedSources: string[],
forceRecompile: boolean
): Promise<Project> {
const removeDuplicates = sourceInfos.reduce((acc: SourceInfo[], sourceInfo: SourceInfo) => {
if (acc.find((info) => info.sourceCodeHash === sourceInfo.sourceCodeHash) === undefined) {
Expand Down Expand Up @@ -558,7 +575,7 @@ export class Project {
scripts,
compilerOptions
)
Project.checkCompilerWarnings(contracts, scripts, changedSources, skipSaveArtifacts, errorOnWarnings)
Project.checkCompilerWarnings(contracts, scripts, changedSources, forceRecompile, errorOnWarnings)
const project = new Project(
contractsRootDir,
artifactsRootDir,
Expand All @@ -568,7 +585,7 @@ export class Project {
structs,
projectArtifact
)
await project.saveArtifactsToFile(projectRootDir, skipSaveArtifacts, changedSources)
await project.saveArtifactsToFile(projectRootDir, forceRecompile, changedSources)
return project
}

Expand All @@ -580,8 +597,8 @@ export class Project {
artifactsRootDir: string,
errorOnWarnings: boolean,
compilerOptions: node.CompilerOptions,
changedSources: SourceInfo[],
skipSaveArtifacts: boolean
changedSources: string[],
forceRecompile: boolean
): Promise<Project> {
const projectArtifact = await ProjectArtifact.from(projectRootDir)
if (projectArtifact === undefined) {
Expand Down Expand Up @@ -612,7 +629,7 @@ export class Project {
}
}

Project.checkCompilerWarnings(contracts, scripts, changedSources, skipSaveArtifacts, errorOnWarnings)
Project.checkCompilerWarnings(contracts, scripts, changedSources, forceRecompile, errorOnWarnings)
return new Project(contractsRootDir, artifactsRootDir, sourceInfos, contracts, scripts, structs, projectArtifact)
} catch (error) {
console.log(`Failed to load artifacts, error: ${error}, try to re-compile contracts...`)
Expand All @@ -625,7 +642,8 @@ export class Project {
artifactsRootDir,
errorOnWarnings,
compilerOptions,
changedSources
changedSources,
forceRecompile
)
}
}
Expand Down Expand Up @@ -749,14 +767,14 @@ export class Project {
contractsRootDir = Project.DEFAULT_CONTRACTS_DIR,
artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR,
defaultFullNodeVersion: string | undefined = undefined,
skipSaveArtifacts = false
forceRecompile = false
): Promise<Project> {
const provider = web3.getCurrentNodeProvider()
const fullNodeVersion = defaultFullNodeVersion ?? (await provider.infos.getInfosVersion()).version
const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir)
const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
const projectArtifact = await ProjectArtifact.from(projectRootDir)
const changedSources = projectArtifact?.getChangedSources(sourceFiles) ?? sourceFiles
const changedSources = projectArtifact?.getChangedSources(sourceFiles) ?? sourceFiles.map((s) => s.name)
if (
projectArtifact === undefined ||
projectArtifact.needToReCompile(nodeCompilerOptions, fullNodeVersion) ||
Expand All @@ -776,7 +794,7 @@ export class Project {
errorOnWarnings,
nodeCompilerOptions,
changedSources,
skipSaveArtifacts
forceRecompile
)
}
// we need to reload those contracts that did not regenerate bytecode
Expand All @@ -789,7 +807,7 @@ export class Project {
errorOnWarnings,
nodeCompilerOptions,
changedSources,
skipSaveArtifacts
forceRecompile
)
}
}
7 changes: 3 additions & 4 deletions packages/cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,13 @@ import {
ContractInstance
} from '@alephium/web3'
import { getConfigFile, loadConfig } from './utils'
import path from 'path'
import { Project } from './project'

export interface Network<Settings = unknown> {
networkId?: number
nodeUrl: string
privateKeys: string[] | string
deploymentStatusFile?: string
deploymentFile?: string
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already support user-defined deployment file paths, so we don't need to introduce a new deployment config.

confirmations?: number
settings: Settings
}
Expand All @@ -60,7 +59,7 @@ export interface Configuration<Settings = unknown> {
networks: Record<NetworkId, Network<Settings>>

enableDebugMode?: boolean
skipSaveArtifacts?: boolean
forceRecompile?: boolean
}

export const DEFAULT_CONFIGURATION_VALUES = {
Expand All @@ -87,7 +86,7 @@ export const DEFAULT_CONFIGURATION_VALUES = {
confirmations: 2
}
},
skipSaveArtifacts: false
forceRecompile: false
}

export interface Environment<Settings = unknown> {
Expand Down
Loading
Loading