Git Product home page Git Product logo

lvsolutionbuilder's Introduction

Solution Builder for LabVIEW

Solution Builder is a tool for building LabVIEW project build specifications in the correct order and, when some of those build specifications generate Packed Project Libraries (PPL, which have a .lvlibp extension), replacement are made in memory to allow dependent components to relink to build output instead of the original source.

Table of Contents

List of Features

Feature DescriptionMinimum Version

Accepts the following inputs:

  • A single project file
  • A directory containing project files to build in 1 session
  • A custom file that defines which projects to build in 1 session

See Usage.

1.0.1

Supports building of all Build Specifications.

Note: Zip files operate on files on disk, not files and their changes currently only in memory.

1.0.1

Does not make a copy of the source before building nor does the tool modify the original source code. Changes are made in memory.

1.0.1

Has mechanism to provides the path to a pre-built Packed Library so its source Library can be replaced at build-time. See Usage.

1.0.1

Supports the use of customized Build Specifications.

1.0.1

Can be invoked from the command-line. See Invoking by Command-line.

1.0.1

Support building the Build Specifications specified under different Targets within the project. Refer to the tests for examples.

1.0.1

Support replacing a PPL with a Target-specific PPL at build-time.

1.0.1

Incremental builds. When building, a .incrb file is generated next to the project/solution file keeping track of files that last built.

1.0.2

Update to the way Targets in a single project are built. Each Target item will now have a temporary project created on disk to enable correctly loading project dependencies that are compiled for that target type. This avoids have issues with previous conflicting dependencies that cannot be unloaded before the next target build. The -KeepSplitProjects input parameter was added to enable debugging if necessary.

Latest source

The -ActiveTarget parameter allows a project with multiple Targets defined to build only selective Targets.

Latest source

Support for the LabVIEW CLI. See Invoking using NI LabVIEW CLI and Enabling the LabVIEW CLI. Requires the NI LabVIEW Command Line Interface to be installed to build/use. This requirement is optional.

Latest source

Specifying a -Version which applies to all build specifications that support a version allowing exports in a session to stay in sync. When no version is specified, the version already in the build specification will be used.

Latest source

Minimum Compatible LabVIEW Version

LabVIEW 2019 SP1 - Solution Builder uses a VI Server method introduced in LabVIEW 2019 to replace project items in memory, and benefits from improvements made to SP1 for in-memory replacement.

This tool is maintained using:

Name Version
LabVIEW 2019 19.0.1f3
LabVIEW Real-Time Module* 2019 f1
NI CompactRIO* 19.6
NI LabVIEW Command Line Interface** 2.1

* These modules only installed to test multi-target build support and are not required for the tool to work as expected.

** The CLI package is only a necessary if you plan to build and use the LabVIEW CLI feature.

Use Case

Common Source File Layout And Dependency Relationships

Source files are commonly organized by Component containing all related files and functionality. Dependencies for source files can span across components given that all the source is on disk when pulling a Git Repo.

Source Files on Disk

The following example shows the file structure of source files.

Component AComponent B
MyAppRepo
└── obj/
└── releases/
└── src/Component A/
	└── myProjectA.lvproj
	└── CompA.lvlib
	└── myProgram.vi (CompA.lvlib:myProgram.vi)
MyAppRepo
└── obj/
└── releases/
└── src/Component B/
	└── myProjectB.lvproj
	└── CompB.lvlib
	└── myFunction.vi (CompB.lvlib:myFunction.vi)

Where CompA.lvlib:myProgram.vi calls and depends on CompB.lvlib:myFunction.vi. Loading both of these source VIs from disks succeeds.

LabVIEW Project Structure

The following example shows the structure of the LabVIEW project files.

myProjectA.lvprojmyProjectB.lvproj
└── My Computer
    └── CompA.lvlib
        └── myProgram.vi (CompA.lvlib:myProgram.vi)
    └── Dependencies
        └── CompB.lvlib
            └── myFunction.vi (CompB.lvlib:myFunction.vi)
    └── Build Specifications
        └── ComponentA (Packed Library)
└── My Computer
    └── CompB.lvlib
        └── myFunction.vi (CompB.lvlib:myFunction.vi)
    └── Dependencies
    └── Build Specifications
        └── ComponentB (Packed Library)
 
 

Where the project Build Specifications have the additional Exclusions as follows:

  • Disconnect type definitions
  • Remove unused polymorphic VI instances
  • Remove unused members of project libraries
  • Exclude dependent packed libraries
  • Exclude dependent shared libraries

Build Artifacts and Layout

Using the current LabVIEW Application Builder, the build results are not in the desired state because the build results are as follows:

Component AComponent B
MyAppRepo
└── obj/
	└── CompA.lvlibp
		└── myProgram.vi (CompA.lvlibp:myProgram.vi)
		└── CompB.lvlib (CompA.lvlibp:CompB.lvlib)
			└── myFunction.vi (CompA.lvlibp:CompB.lvlib:myFunction.vi)
└── src/Component A/
MyAppRepo
└── obj/
	└── CompB.lvlibp
		└── myProgram.vi (CompB.lvlibp:myFunction.vi)
└── src/Component B/
 
 

Where Component B is correctly built because it has not dependencies, but notice that Component A pulled in its own copy of its dependency where CompA.lvlibp:myProgram.vi calls and depends on CompA.lvlibp:CompB.lvlib:myFunction.vi which is not what we want.

Note: The exception exists when this is the desired state for shared helper code that, for isolution from revisional and breaking changes, we do want to pull in that namespaced copy.

Instead, for major components that are versioned like in our example, the desired state is that when Component A builds it switches its dependency to the built packed libary of Component B at build-time resulting in the following:

Component AComponent B
MyAppRepo
└── obj/
	└── CompA.lvlibp
		└── myProgram.vi (CompA.lvlibp:myProgram.vi) -- depends on -->
└── src/Component A/
MyAppRepo
└── obj/
	└── CompB.lvlibp
		└── myFunction.vi (CompB.lvlibp:myFunction.vi)
└── src/Component B/

Where CompA.lvlibp:myProgram.vi calls and depends on CompB.lvlibp:myFunction.vi looking similar to source namespacing and component boundaries.

Cascading Packed Library Builds

To accomplish the correct build result the build needs to calculate the build order and then replace links to build results before building the next caller in the following fashion.

  1. Analyze projects and their dependencies
  2. Order the build specifications bottom-up
  3. Build the first items with no dependencies
  4. Find the next level that needs to build, whose dependencies have been built
  5. Relink that next level to depend on the built components
  6. Build that next level
  7. Repeat the process from step 3 until there is nothing left to build.

Multiple PPLs and Build Specification in a Single Project

The following example shows the structure of the LabVIEW project files and result when a single project contains multiple libraries that depend on each other and produce separate PPLs.

myProjectA.lvproj (Source)Component A (Build Result)
└── My Computer
    └── CompA.lvlib
        └── myProgram.vi (CompA.lvlib:myProgram.vi)
    └── subCompC.lvlib
        └── myFunctionC.vi (subCompC.lvlib:myFunctionC.vi)
    └── subCompD.lvlib
        └── myFunctionD.vi (subCompD.lvlib:myFunctionD.vi)
    └── Dependencies
        └── CompB.lvlib
            └── myFunction.vi (CompB.lvlib:myFunction.vi)
    └── Build Specifications
        └── ComponentA (Packed Library)
        └── subComponentC (Packed Library)
        └── subComponentD (Packed Library)
MyAppRepo
└── obj/
	└── CompA.lvlibp
		└── myProgram.vi (CompA.lvlibp:myProgram.vi)
	└── subCompC.lvlibp
		└── myFunctionC.vi (subCompC.lvlibp:myFunctionC.vi)
	└── subCompD.lvlibp
		└── myFunctionD.vi (subCompD.lvlibp:myFunctionD.vi)
└── src/Component A/
└── src/subComponent C/
└── src/subComponent D/



Usage

Solution Builder can be operated on through its user interface or by command-line.

Solution Builder UI

  • Path : Path to the file or folder to execute on. The following are accepted:
ExampleExplanation
C:\This\FolderFolder containing .lvproj files (all files will be processed as one)
C:\This\Folder\Project.lvprojA single .lvproj file (only this file will be processed)
C:\This\Folder\solution file.slnfileA file (the extension is ignored) listing the projects to build and any associated pre-built PPLs needed to build the projects.)
  • Log file path : Path to the file containing log information regarding decisions to rebuild an item or skip it because a previous build was deemed sufficient.

  • Preview : Will run the algorithm to determine the dependency build order, but will not execute the projects' build specification.

  • Force Rebuild : Ignores the incremental build information from previous runs and rebuilds everything.

  • Cancel Build : Aborts a build, but waits for the current item to complete.

  • Results Table : Displays the found build specification outputs and show their build status.

  • Error/Warning Bar : Return any build errors or status from the utility.

When using a solution file, the following is an example valid content tags:

<Solution>
	<ProjectPath></ProjectPath>
	<AddPackedLib></AddPackedLib>
	<Version></Version>
</Solution>
TagExplanation
SolutionRoot node in the file
ProjectPathNode that contains a path value used by solution builder to find build specifications. Multiple ProjectPath tags can be listed in a file. Valid path formats are as follows:
	<ProjectPath>C:\This\Is\A Windows Absolute Path.lvproj</ProjectPath>
	<ProjectPath>Relative\Path.lvproj</ProjectPath>
	<ProjectPath>..\Relative\Path.lvproj</ProjectPath>
	<ProjectPath>Relate Path/Forward Slash.lvproj</ProjectPath>
	<ProjectPath>../Relate Path/Forward Slash.lvproj</ProjectPath>
	<ProjectPath>/C/Linux/Absolute/Path.lvproj</ProjectPath>
AddPackedLib (Optional)Node that contains a path value to a pre-built PPL to be used during the specified build. Multiple AddPackedLib tags can be listed in a file. Additionally and optionally, when a source Library is named differently than its PPL counterpart, and the Target under which to make this substitution can be listed. The following sub-token are supported:
  • PATH= Path to specify the path to the build PPL. This sub-token is required.
  • NAME= Name of the original library. This sub-token is optional. If no name is listed, the filename from the path will be used, minus the "p" in the extension. ThisLib.lvlibp will become ThisLib.lvlib.
  • TARGET= Name of the project's TargetItem. This sub-token is optional. If not Target is listed, My Computer will be used.

Valid formats are as follows and the path can be formated similar to ProjectPath paths:

	<AddPackedLib>PATH=Relative\Path.lvlibp</AddPackedLib>  # Will implicitly append "::NAME=Path.lvlib::TARGET=My Computer" 
	<AddPackedLib>PATH=..\Relative\Path.lvlibp::NAME=OriginalName.lvlib</AddPackedLib>  # Will implicitly append "::TARGET=My Computer" 
	<AddPackedLib>PATH=..\Relative\OtherPath.lvlibp::NAME=OriginalName.lvlib::TARGET=cRIO-9082</AddPackedLib>
Version (Optional)Node that contains the version to apply to all build specifications that support a version. Valid version formats are as follows:
	<Version>1.2</Version>
	<Version>1.2.3</Version>
	<Version>1.2.3.4</Version>

Invoking using NI LabVIEW CLI

Begin by either downloading the latest Release or building the latest source, and the following then Enabling the LabVIEW CLI.

Invoking the CLI requires a command similar to the following (See List of accepted arguments for details):

:> LabVIEWCLI -OperationName BuildSolution -Path <path_to_some_project>\myProject.lvproj -LogFile <path_to_some_log>\file.log -AddPackedLib PATH=<path_to_some_ppl>\TheFile.lvlibp::NAME=TheOriginalLib.lvlib::TARGET=cRIO-9068 -Rebuild -ActiveTarget "My Computer" -ActiveTarget cRIO-9082 -KeepSplitProjects -Version 1.2.3.4

To invoke the CLI with a custom path to the BuildSolution operation using the -AdditionalOperationDirectory parameter detailed in Enabling the LabVIEW CLI Option 2.

Invoking by Command-line

Begin by either downloading the latest Release or building the latest source.

Note: It's important to close LabVIEW between invocations of the tool from the command-line since LabVIEW only reads command-line arguments on launch. To read the new command-line arguments, LabVIEW must close and relaunch with the new arguments. To help with exiting LabVIEW between calls, use the -Quiet option.

Invoking the packed tool from its built LLB requires a command similar to the following (See List of accepted arguments for details):

Path_to_repo> "C:\program files\national instruments\LabVIEW 2020\LabVIEW.exe" <path_to_llb_obj>\SolutionBuilder.llb\SolutionBuilder.vi -- -Path <path_to_some_project>\myProject.lvproj -LogFile <path_to_some_log>\file.log -Quiet -AddPackedLib PATH=<path_to_some_ppl>\TheFile.lvlibp::NAME=TheOriginalLib.lvlib::TARGET=cRIO-9068 -Rebuild -ActiveTarget "My Computer" -ActiveTarget cRIO-9082 -KeepSplitProjects -Version 1.2.3.4

List of accepted arguments

When using either the LabVIEW CLI or directly using the command-line, the following arguments are accepted:

Command Description
-Path <path> Path to the folder, project, or solution file. Refer to the Path token section for details.
-LogFile <path> Path to the file containing log information regarding decisions to rebuild an item or skip it because a previous build was deemed sufficient.
-AddPackedLib <path> Path to a pre-built PPL. Refer to the AddPackedLib token section for details.
-Quiet Auto close once build as completed. Should not be used with -Preview. This mode is ignored when using the LabVIEW CLI.
-Preview Does not build but instead displays the list and build order of each build specification. Recommended for validation.
-Rebuild Ignores the incremental build information from previous runs and rebuilds everything.
-ActiveTarget <targetname> Specifies the Target under which the build specifications will build. Can specify many.
-KeepSplitProjects Skips the split project clean-up step at the end of the build.
-Version <version_string> Specifies a version to apply to all build specifications that support a version. When no version is specified, the version already in the build specification will be used. To ensure consistency when building to match component versions, using the -Rebuild flag with this option is recommended. Additionally, if a Version is specified in multiple places (the UI, the command-line, a .slnbld file) the .slnbld file wins. String is in the format major.minor.fix.build

Incremental Builds

Solution Builder supports some incremental builds. This means that the tool will do its best to determine if a Project Build Specification needs to rebuild. The algorithm to determine if an build specifation needs to rebuild is as follows:

  1. The Rebuild flag is set to true either from the command line or from the UI.
  2. The destination of the build output does not exist.
  3. The source files associated with the build specification have changed.
  4. Static dependencies of the source files associated with the build specification have changed.

Note: the tool determines that a source file has changed if the MD5 hash of a file does not match its previous hash. Hashes from previous runs are stored in a .incrb file next to the project/solution file.

Limitations of Incremental Builds

Source distribution currently do not know before they build which source files they are associated with. As a result, source distributions always rebuild.

How to Build

The owning LabVIEW project contains a build specification to create a self-contained LLB.

  1. Open the project
  2. Expand the Build Specification entry in the project and build the component you want. Attempting to build the LabVIEW CLI build specification requires NI LabVIEW Command Line Interface to be installed; building the other components without the CLI installed is supported.

Once built, the built binaries will be located in the \LVSolutionBuilder\obj folder. The contents of the obj directory are as follows:

Item/Folder Description
SolutionBuilder.llb The main LLB that contains Solution Builder and all its subVIs. SolutionBuilder.vi is listed as a Top-Level VI in the LLB.
Package (folder) An NI Package containing SolutionBuilder.llb.
LabVIEW CLI (folder) Contains folders and files that can be placed in National Instruments\Shared\LabVIEW CLI to enable the LabVIEW CLI functionality. The folder structure matches the files destination. (Requires NI LabVIEW Command Line Interface to be installed)

Enabling the LabVIEW CLI

There are 2 ways of invoking Solution Builder with the LabVIEW CLI. The first involves copying the built files to the CLI's known location (option 1). The second involves invoking the CLI with a custom path telling it where the built binaries can be found (option 2).

Option 1: After following the instructions in the How to build section:

  1. Copy the obj\LabVIEW CLI folder to <program_files>\National Instruments\Shared to merge the built folder with the existing folder installed by LabVIEW.
  2. Copy the obj\SolutionBuilder.llb to the new <program_files>\National Instruments\Shared\LabVIEW CLI\Operations\BuildSolution folder.
    • Note: To support multiple versions of LabVIEW, place the SolutionBuilder.llb inside a year-version subfolder. For example, .\BuildSolution\2020\SolutionBuilder.llb, etc. If no matching year-version subfolder is found, the CLI will look for the LLB in the root .\BuildSolution folder.

Option 2: Simply invoke the CLI while adding the following argument: -AdditionalOperationDirectory "<path_to_llb_obj>\LabVIEW CLI\Operations\BuildSolution".

How to Test

Open and run /src/_test/RunTheTests.vi then verify that the results all pass.

Note: To avoid the issue listed in the Known Issues, run the main test VI directly instead of from the owning project.

Contributions

This project welcomes Issues, Discussions, and Pull Requests. Review the CONTRIBUTING document for information.

Known Issues

IDIssueWorkaround
1

When running SolutionBuilder.vi from its parent project, if the application returns an error, there is a possibility that on the next runs an LV Application Builder error will be returned for reasons unexplained. The error is:

Error 1370 occurred at an unidentified location
Possible reason(s):
LabVIEW: (Hex 0x55A) The selected build failed to complete.

There are a few ways around the issue:

  • Exit LabVIEW and retry to build.
  • Instead of running SolutionBuilder from its owning project, open the VI directly from `/src`.
  • Instead of running SolutionBuilder from its owning project, open the VI directly from the built release LLB.
2

There is a LabVIEW bug preventing the discovery of all libraries needing to be replaced in memory introduced in LabVIEW 2020 SP1 onward. It it takes special circumstances to manifest:

  1. A caller VI just use a dynamic dispatch VI of a child class that does not have it's own implementation,
  2. The caller VI must not reference the parent class directly
  3. The caller VI must be compiled in order for its BD to load when the VI is loaded.

Fixed by NI

NI has fixed the issue in a recent patch of LV 2020 SP1 (Released 09-Nov-2021). See the list of bug fixes bug number 1707381.

Update to the most recent patch if you are running into this issue.

lvsolutionbuilder's People

Contributors

jovianarts avatar niphilj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

lvsolutionbuilder's Issues

Some tests fail on "main" branch

The test for "BuildAdvancedAssets.vi" fails and the test for "BuildAssetsIncrementBuild.vi" errors based on the test executor utility.

image

No Targets support

When trying to build libraries that are on other Targets (other than "/My Computer", especially when these are libraries that are shared between Targets, only one of them gets recognized.

Some Project Items Generate Without Source File

This is related to another issue I recently submitted, but I found when I run the LV Solution Builder with the slnfile method and point at a project with a single build spec for an executable, 4 project items are generated. Only one of these project items has dependencies listed, and it is also the only item with a source file listed.

I have found via testing that the way SubGenerateListToOrder.vi currently parses project items and adds them to the ordering map results in one of the project items without a source file to be used for my build spec, and the build ends up failing after all libraries have been swapped. This issue was resolved locally by adding code that ensured the project item with a source file (which was the top-level VI included in my build specification), but I wanted to report this as an issue in case one build spec generating four project items would be considered unexpected behavior (especially with 3/4 items not containing a source file).

PPLs Included in SLNFILE Replacement Order

Including PPLs using the slnfile method results in the PPLs being swapped in alphabetical order (due to them being stored in a set), but the order of replacement was significant in my testing.

I found that if I replaced the PPLs in the order they were created in, the build succeeded, but the build failed if I attempted to replace alphabetically.

Can a feature be added to programmatically determine the dependency order of previously created PPLs?

Not all VI's in memory when project opens

When a LabVIEW project is opened, not all VI's within the project are immediately loaded into memory. I believe this becomes an issue with the LabVIEW Solution Builder because when the Replace With operation attempts to keep the changes in memory, the VI's that are not immediately loaded into memory immediately leave memory after the Replace With operation completes. This results in the potential coexistence of the lvlib and lvlibp versions of the dependent libraries in the dependencies for a project that may not always cause errors that break the build.

This issue can be recreated by manually tracing the process that the Solution Builder follows, and to the best of my knowledge there is no code in the Solution Builder that ensures the VI's I referenced above stay in memory until the build is complete. For this reason, I believe it would make sense to add a feature that ensures all VI's within a project are fully loaded into memory before the replace operation and that they remain in memory until the build it complete.

I welcome any feedback on this idea. Please let me know if you have any questions or if you would like me to provide a specific test case. Thank you.

[Enhancement] Exposing "Folders to Exclude" while Populating Project List

Hey @jovianarts , @niphilj ,

Here is a minor enhancement proposal:
Exposing "Folders to Exclude" option while getting the list of LabVIEW Project Files from a Directory.

Reason for the Request:
In our use case, we have few template project files which are getting built, when the top level directory is selected. It would be useful if the "Folders to Exclude" option is exposed, so that we can exclude some folders.

Place to Change:
SolutionBuilder.llb\subFindProjects.vi
image
We need to expose Folders to Exclude in above VI.

I can also make this change if this enhancement is approved :)

Exposing Optional Argument for Build Version

I have a use case where I am calling the Solution Builder via the command line, and I would like to have the ability to specify the build version of the object(s) that I am building. This would provide me with more control over the versioning instead of relying solely on the "Auto-increment version on build" functionality of each build spec. This would also allow for incrementing the major/minor/patch versions in addition to the build version without having to manually update each build spec.

This may also make sense to expose in the Solution File use case (if that handling is different).

Multiple Project Items for One Build Spec (Only One Carries Deps)

When I point an slnfile at a single project with one build spec, four different project items are generated, but only one of them carries the proper dependencies.

The four different project items are in the attached screenshots. Only the second item seems to be "right", or at least this is the item that I was expecting when I started debugging.

Due to how the ordering map is generated in SubGenerateListToOrder.vi, the dependencies of the "right" project item were added to the map for the purpose of build ordering, but the dependencies are left out of the project item that is actually built, which results in the libraries not being swapped.

Extra code can be added to the in place element structure of SubGenerateListToOrder.vi to add dependencies to any map elements that are being modified to ensure the full list is maintained (I have tested this method and confirmed its validity), but is this the right way to approach this issue?

image

image

image

image

Need way to only build specific build(s) in a project

This tool has helped a ton getting a project off the ground. But now my project is in the state that I am only making small updates, usually only affecting 1 of the build specifications (currently 27 builds in this project, all PPLs). What I need is a way to specify the specific build(s) that actually need built and only build those few instead of all of the build specifications.

Build path incorrectly computed when it's in the same hierarchy as its owning project

Writing this on behalf of a user who reached out to me directly:

We think the first StripPath in the subVI subConvertValueToNameAndPath.vi should be removed. Without removing this, the Builder could not locate previously created .lvlibp’s. Our folder structure looked like:

A folder/
   Package A.lvproj
   Package B.lvproj
   Packages/
      Package A.lvlibp
      Package B.lvlibp

Build order not calculating properly

I am attempting to use the Solution Builder to build a library that makes use of the Metronome class that is included in the JDP Science Messenger Library package. The Metronome class is not a member of a library, but it utilizes Parallel Process.lvlib.

When I point the Solution Builder at a directory containing build specs for Parallel Process.lvlib and for a sample library containing a VI that utilizes Metronome.lvclass, the builder attempts to build the sample library first despite this library's dependence on Parallel Process.lvlib (see attached screenshot).

I have attached the directory I am trying to build to aid in recreating this issue and reviewing the dependencies for each project.

Wrong Build Order

Test.zip

Handling of Package Building Over PPLs | v1.0.2

Hey Phil,

Thank you for this awesome tool! This tool has been very helpful in working with multiple dependent PPLs and makes the developer life lot easier.

One question - Does the current version of the tool (v1.0.2) support building of NI Packages which is used for deploying PPLs? Say, I have a LabVIEW Project with 2 build specs - one PPL and one NI Package (which is used for deploying the PPL). When the PPL gets built, will it also trigger and build its dependent Package as well?

I looked into the code a bit and found that packages are stored separately as a 2-tier item but the flag for rebuilding is not set for them. Due to this, tier 2 build specs like NI Packages and Installers are getting skipped. I assume this feature is in development and not available yet.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.