diff --git a/extensions/sql-database-projects/src/common/constants.ts b/extensions/sql-database-projects/src/common/constants.ts index f0b01a2cb2..3daf3dbead 100644 --- a/extensions/sql-database-projects/src/common/constants.ts +++ b/extensions/sql-database-projects/src/common/constants.ts @@ -328,6 +328,7 @@ export const ItemGroup = 'ItemGroup'; export const Build = 'Build'; export const Folder = 'Folder'; export const Include = 'Include'; +export const Remove = 'Remove'; export const Import = 'Import'; export const Project = 'Project'; export const Condition = 'Condition'; diff --git a/extensions/sql-database-projects/src/models/project.ts b/extensions/sql-database-projects/src/models/project.ts index 25e7477ee0..b0e5aa4847 100644 --- a/extensions/sql-database-projects/src/models/project.ts +++ b/extensions/sql-database-projects/src/models/project.ts @@ -148,27 +148,50 @@ export class Project implements ISqlProject { // find all folders and files to include that are specified in the project file try { const buildElements = itemGroup.getElementsByTagName(constants.Build); + + // for (let b = 0; b < buildElements.length; b++) { const relativePath = buildElements[b].getAttribute(constants.Include)!; - const fullPath = path.join(utils.getPlatformSafeFileEntryPath(this.projectFolderPath), utils.getPlatformSafeFileEntryPath(relativePath)); - // msbuild sdk style projects can handle other globbing patterns like and - if (this._isMsbuildSdkStyleProject && !(await utils.exists(fullPath))) { - // add files from the glob pattern - const globFiles = await utils.globWithPattern(fullPath); - globFiles.forEach(gf => { - const newFileRelativePath = utils.convertSlashesForSqlProj(utils.trimUri(Uri.file(this.projectFilePath), Uri.file(gf))); - if (!this._files.find(f => f.relativePath === newFileRelativePath)) { - this._files.push(this.createFileProjectEntry(utils.trimUri(Uri.file(this.projectFilePath), Uri.file(gf)), EntryType.File)); + if (relativePath) { + const fullPath = path.join(utils.getPlatformSafeFileEntryPath(this.projectFolderPath), utils.getPlatformSafeFileEntryPath(relativePath)); + + // msbuild sdk style projects can handle other globbing patterns like and + if (this._isMsbuildSdkStyleProject && !(await utils.exists(fullPath))) { + // add files from the glob pattern + const globFiles = await utils.globWithPattern(fullPath); + globFiles.forEach(gf => { + const newFileRelativePath = utils.convertSlashesForSqlProj(utils.trimUri(Uri.file(this.projectFilePath), Uri.file(gf))); + if (!this._files.find(f => f.relativePath === newFileRelativePath)) { + this._files.push(this.createFileProjectEntry(utils.trimUri(Uri.file(this.projectFilePath), Uri.file(gf)), EntryType.File)); + } + }); + } else { + // only add file if it wasn't already added + if (!this._files.find(f => f.relativePath === relativePath)) { + this._files.push(this.createFileProjectEntry(relativePath, EntryType.File, buildElements[b].getAttribute(constants.Type)!)); } - }); + } + } + } - // TODO: add support for + // + // after all the files have been included, remove the ones specified in the sqlproj to remove + if (this._isMsbuildSdkStyleProject) { + for (let b = 0; b < buildElements.length; b++) { + const relativePath = buildElements[b].getAttribute(constants.Remove)!; - } else { - // only add file if it wasn't already added - if (!this._files.find(f => f.relativePath === relativePath)) { - this._files.push(this.createFileProjectEntry(relativePath, EntryType.File, buildElements[b].getAttribute(constants.Type)!)); + if (relativePath) { + const fullPath = path.join(utils.getPlatformSafeFileEntryPath(this.projectFolderPath), utils.getPlatformSafeFileEntryPath(relativePath)); + + const globRemoveFiles = await utils.globWithPattern(fullPath); + globRemoveFiles.forEach(gf => { + const removeFileRelativePath = utils.convertSlashesForSqlProj(utils.trimUri(Uri.file(this.projectFilePath), Uri.file(gf))); + + if (this._files.find(f => f.relativePath === removeFileRelativePath)) { + this._files = this._files.filter(f => f.relativePath !== removeFileRelativePath); + } + }); } } } diff --git a/extensions/sql-database-projects/src/test/baselines/baselines.ts b/extensions/sql-database-projects/src/test/baselines/baselines.ts index d0c57243c5..bccee2ac01 100644 --- a/extensions/sql-database-projects/src/test/baselines/baselines.ts +++ b/extensions/sql-database-projects/src/test/baselines/baselines.ts @@ -33,6 +33,7 @@ export let newStyleProjectSdkImportAttributeBaseline: string; export let openNewStyleSqlProjectBaseline: string; export let openNewStyleSqlProjectWithFilesSpecifiedBaseline: string; export let openNewStyleSqlProjectWithGlobsSpecifiedBaseline: string; +export let openNewStyleSqlProjectWithBuildRemoveBaseline: string; const baselineFolderPath = __dirname; @@ -63,6 +64,7 @@ export async function loadBaselines() { openNewStyleSqlProjectBaseline = await loadBaseline(baselineFolderPath, 'openNewStyleSqlProjectBaseline.xml'); openNewStyleSqlProjectWithFilesSpecifiedBaseline = await loadBaseline(baselineFolderPath, 'openNewStyleSqlProjectWithFilesSpecifiedBaseline.xml'); openNewStyleSqlProjectWithGlobsSpecifiedBaseline = await loadBaseline(baselineFolderPath, 'openNewStyleSqlProjectWithGlobsSpecifiedBaseline.xml'); + openNewStyleSqlProjectWithBuildRemoveBaseline = await loadBaseline(baselineFolderPath, 'openNewStyleSqlProjectWithBuildRemoveBaseline.xml'); } async function loadBaseline(baselineFolderPath: string, fileName: string): Promise { diff --git a/extensions/sql-database-projects/src/test/baselines/openNewStyleSqlProjectWithBuildRemoveBaseline.xml b/extensions/sql-database-projects/src/test/baselines/openNewStyleSqlProjectWithBuildRemoveBaseline.xml new file mode 100644 index 0000000000..2565aec26e --- /dev/null +++ b/extensions/sql-database-projects/src/test/baselines/openNewStyleSqlProjectWithBuildRemoveBaseline.xml @@ -0,0 +1,32 @@ + + + + + TestProjectName + {2C283C5D-9E4A-4313-8FF9-4E0CEE20B063} + Microsoft.Data.Tools.Schema.Sql.Sql150DatabaseSchemaProvider + 1033, CI + + + + + + + + + + + + + + + + False + master + + + False + master + + + diff --git a/extensions/sql-database-projects/src/test/project.test.ts b/extensions/sql-database-projects/src/test/project.test.ts index 353332ed37..b64c299584 100644 --- a/extensions/sql-database-projects/src/test/project.test.ts +++ b/extensions/sql-database-projects/src/test/project.test.ts @@ -914,6 +914,38 @@ describe('Project: Msbuild sdk style project content operations', function (): v // make sure no duplicates from folder1\*.sql should(project.files.filter(f => f.relativePath === 'folder1\\file1.sql').length).equal(1); }); + + it('Should handle Build Remove in sqlproj', async function (): Promise { + const testFolderPath = await testUtils.generateTestFolderPath(); + const mainProjectPath = path.join(testFolderPath, 'project'); + const otherFolderPath = path.join(testFolderPath, 'other'); + projFilePath = await testUtils.createTestSqlProjFile(baselines.openNewStyleSqlProjectWithBuildRemoveBaseline, mainProjectPath); + await testUtils.createDummyFileStructure(false, undefined, path.dirname(projFilePath)); + + // create files outside of project folder that are included in the project file + await fs.mkdir(otherFolderPath); + await testUtils.createOtherDummyFiles(otherFolderPath); + + const project: Project = await Project.openProject(projFilePath); + + should(project.files.filter(f => f.type === EntryType.File).length).equal(6); + + // make sure all the correct files from the globbing patterns were included and excluded + // + // + should(project.files.filter(f => f.relativePath === '..\\other\\folder1\\file1.sql').length).equal(0); + should(project.files.filter(f => f.relativePath === '..\\other\\folder1\\file2.sql').length).equal(1); + + // + should(project.files.filter(f => f.relativePath === 'folder1\\file1.sql').length).equal(0); + should(project.files.filter(f => f.relativePath === 'folder1\\file2.sql').length).equal(0); + should(project.files.filter(f => f.relativePath === 'folder1\\file3.sql').length).equal(0); + should(project.files.filter(f => f.relativePath === 'folder1\\file4.sql').length).equal(0); + should(project.files.filter(f => f.relativePath === 'folder1\\file5.sql').length).equal(0); + + // + should(project.files.filter(f => f.relativePath === 'file1.sql').length).equal(0); + }); }); describe('Project: add SQLCMD Variables', function (): void {