Click here to Skip to main content
14,976,314 members
Articles / Programming Languages / PowerShell
Article
Posted 4 Feb 2019

Tagged as

Stats

8.1K views
78 downloads
17 bookmarked

How to Manually Create NuGet Package

Rate me:
Please Sign up or sign in to vote.
4.50/5 (3 votes)
4 Feb 2019CPOL9 min read
This article will help you understand how to create NuGet package manually. It will demonstrate how to add a simple C# code file, making changes in app.config file, run powershell script and also add a dependency project.

Introduction

NuGet packages provide flexibility to add third party libraries with lesser effort and time, and avoid delving into complex, time consuming and error-prone activities may be required by the target library you want to add in your project. In this article, I will explain how to create NuGet package manually. Although there are many things to consider when creating a package as per the requirements of any particular package, I am not going into details of all those configuration options. It will restrict the discussion with the minimum points that need to explain the fundamentals of creating a NuGet Package. You will learn how to:

  • Add a simple C# code file
  • Make changes in app.config file (or web.config)
  • Add a dependency project (log4net)
  • Execute Powershell script on Install & Uninstall

Background

While there are thousands of packages available publicly free of cost, you may be wondering why we need to create our own package. There could be many reasons like you need to share your own custom library across your company projects or even publicly, you may need to make small functional changes of an existing package, or you may just want to keep a log of all your useful code classes or libraries in different repositories so that might help you in future projects to save time of re-writing.

This article is using log4net library as dependency package, let's take a look at this first.

log4net (A Dependency for Our Package)

If you have ever considered a logging framework or library, you definitely came across log4net. log4net is a tool to help the programmer output log statements to a variety of output targets. It offers a list of appenders which you can use according to your requirements. Some of the appenders are:

  • AdoNetAppender
  • AnsiColorTerminalAppender
  • AspNetTraceAppender
  • ColoredConsoleAppender
  • ConsoleAppender
  • EventLogAppender
  • FileAppender
  • NetSendAppender
  • RemoteSyslogAppender
  • RemotingAppender
  • RollingFileAppender
  • SmtpAppender
  • SmtpPickupDirAppender
  • TelnetAppender
  • UdpAppender

You can find more information at What is Apache log4net.

Our Package Overview

Generally, Nuget package is created from .NET Framework Class Library. In our example, we use a C# Class Library project. I will create a package which provides a C# class CustomLog_Appender inheriting AppenderSkeleton class of log4net library. Of course, this package is not very useful but it will clarify certain concepts which you need to understand for creating a new package. I want to implement my own target and logic for storing log details, let's say I need to send log messages to my WCF service which is not available in default Appenders list. I am creating this package so that whenever I will have similar requirement, I will install this package and it will give me a template class of custom appender and I will just start writing my own code of calling my WCF service without digging into further configuration of log4net. Hence, this package is basically a wrapper around log4net library to provide an appender class.

Create Content for the Package

Let's start creating content for your package. As I mentioned earlier, we will create the following content items to include in our package:

  1. Add a simple C# code file - Although we need to ship only single C# file in package installation, we will create this file in C# Class Library project.
  2. Make changes in app.config file (or web.config) - Stand-alone config files will be enough for the purpose of this article, you can use any text-editor to create these files.
  3. Add a dependency project (log4net) - We don't have to do anything with this library, only we have make an entry in .nuspec file to instruct the Package Manager to install this as dependency.
  4. Execute Powershell script on Install & Uninstall - We will add two separate powershell script files to execute when this package will be installed or uninstall

1. Our Project (C# Class Library)

Create a new project with C# Class Library (.NET Framework) project template. Add a new folder Model, add new class named CustomLog_Appender with the following code listing:

C#
using log4net.Appender;
using log4net.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1.Model
{
    public class CustomLog_Appender : AppenderSkeleton
	{
        ////My service object
        //MyLoggerService loggerService = new MyLoggerService();

        protected override void Append(LoggingEvent loggingEvent)
        {
            ////call service's LogMessage method
            //loggerService.LogMessage(loggingEvent.UserName, 
            //loggingEvent.Level.DisplayName, loggingEvent.TimeStamp, 
            //loggingEvent.RenderedMessage);
        }
    }
}

This class is inherited from AppenderSkeleton base class and overrides the method Append which is receiving LoggingEvent object as parameter. This method will be invoked whenever you call one of logger's logging methods (Info(), Debug(), Warn(), Error(), Fatal()) in your code. Inside this method, we are passing the required information to the WCF service's client object. This is the code file which we need to add in your package so that when this package will be installed in any other project, it will add this C# file by itself.

Rename the file name by appending .pp at the end, e.g., CustomLog_Appender.cs will become CustomLog_Appender.cs.pp.

2. Make Changes in app.config File

Log4net library needs you to provide custom section in app.config (or web.config, procedure is the same in both config files). Since we are using a custom appender class, we have to tell log4net library to use this class as the appender. I am using XDT syntax to transform config file. Text from docs.microsoft.com:

Using any text-editor, you need to create app.config.install.xdt and web.config.install.xdt files in your package's content folder, with XDT syntax to describe the desired changes. Using similar syntax, you can also include a .uninstall.xdt file to reverse the changes when the package is removed from a project.

Here is the content of app.config.install.xdt:

XML
		<!--?xml version="1.0"?-->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">

	<configSections xdt:Transform="InsertIfMissing">
	</configSections>

	<configSections>
		<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, 
                                            log4net" xdt:Transform="Insert"/>
	</configSections>

	<log4net xdt:Transform="Insert">
		<root>
			<level value="ALL" />
			<appender-ref ref="CustomLog4Appender" />
		</root>
		<appender name="CustomLog4Appender" 
               type="ConsoleApplication1.Model.CustomLog_Appender">
		</appender>
	</log4net>  
	
</configuration>		

And here is the content of app.config.uninstall.xdt:

XML
<!--?xml version="1.0"?-->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
		  
	<configSections>
		<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, 
                       log4net" xdt:Transform="Remove" xdt:Locator="Match(name)"/>
	</configSections>

	<log4net xdt:Transform="Remove">
		<root>
			<level value="ALL" />
			<appender-ref ref="CustomLog4Appender" />
		</root>
			<appender name="CustomLog4Appender" 
                   type="ConsoleApplication1.Model.CustomLog_Appender">
		</appender>
	</log4net>  

</configuration>		

Place both files in a directory named "content" at project's root.

3. Execute Powershell Script on Install & Uninstall

As per log4net requirements, we have to add the following line at the bottom of the AssemblyInfo.cs file.

[assembly: log4net.Config.XmlConfigurator(Watch = true)]		

To add this line, I am using Powershell script, which will append this line when our package is installed and delete it when the package will be uninstalled. We will create two powershell script files inside "content" folder.

Create file Install.ps1 with the following content, this will add log4net config line in AssemblyInfo.cs file.

param($installPath, $toolsPath, $package, $project)

$lineText = "`r`n[assembly: log4net.Config.XmlConfigurator
                           (Watch = true)]" + " // Added by: (" + $package.Id + ")"
$file1 = Split-Path (Get-Project).FileName
$file1 = $file1 + "\Properties\AssemblyInfo.cs"
			 
Add-Content $file1 $lineText		

Create file Uninstall.ps1 with the following content, this will delete log4net config line from AssemblyInfo.cs file.

param($installPath, $toolsPath, $package, $project)

$lineText = "[assembly: log4net.Config.XmlConfigurator
            (Watch = true)]" + " // Added by: (" + $package.Id + ")"
$file1 = Split-Path (Get-Project).FileName
$file1 = $file1 + "\Properties\AssemblyInfo.cs"
			 
(Get-Content $file1 ) -split '`r`n' | Where-Object {$_ -ne $lineText } | Set-Content $file1		

Let's Start Creating Our Package

First, you need to download and install nuget.exe. And put the location in installed directory in environment PATH variable or copy it in the root directory (where solution file .sln exists) of the project.

Open the command prompt and move to the project directory, and run the following command:

nuget spec

The above command will create a file named Package.nuspec. This is the main component of Nuget package. This is an XML file that represents a package, it contains all the information that requires to publish. Open Package.nuspec file in text editor and update any required field values in XML. Here is the updated XML file which I edited according to this example.

XML
<!--?xml version="1.0"?-->
<package >
  <metadata>
    <id>Log4net.CustomLog_Appender</id>
    <version>1.0.0</version>
    <authors>midrees</authors>
    <owners>midrees</owners>
    <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
    <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
    <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>This is Log4net custom appender</description>
    <releaseNotes>Add a CustomAppender class with empty Append() method, 
     you can write your own code to implement log writing mechanism in this method.</releaseNotes>
    <copyright>Copyright 2019</copyright>
    <tags>Log4net CustomAppender</tags>
    <dependencies>
      <dependency id="log4net" version="2.0.8" />
    </dependencies>
  </metadata>
  <files>  
	    <file src="readme.txt" target=""/>
	    <file src="ConsoleApplication1\Model\CustomLog_Appender.cs.pp" target="content\Log4net"/>
		<file src="content\app.config.install.xdt" target="content" />
		<file src="content\app.config.uninstall.xdt" target="content" />
		<file src="content\Install.ps1" target="tools\Install.ps1" />
		<file src="content\Uninstall.ps1" target="tools\Uninstall.ps1" />
	</files>
</package>

Following are the elements you need to specify according to your package.

Element Description
id The case-insensitive package identifier. It must be unique if you want to host on Nuget.org. For the purpose of this article, we can use any desired id.
version The version of the package (in major.minor.patch pattern). You need to update this value for each subsequent package deployment.
description Description of the package
authors A comma-separated list of packages authors
projectUrl A URL for your package's home page.
licenseUrl A URL for your package's license, if you want to have
iconUrl A URL for an image/icon for the package
releaseNotes A brief description of the changes made in this release
copyright Copyright details for your package
tags Space separated list of tag you may want to attach with your package.
dependencies This is optional and may contain one or more dependencies your package may need. In your case, we are having one entry for log4net.
files List of files you need to copy to the target project where this package will be installed.

Let's Review the Files Tag

This is the tag from Package.nuspec file, where we have to include the content files for our package. The content files we have created in the above sections. For most file entries, you have to specify src and target attributes, except the readme.txt file which we don't want to copy to the target project, instead we only want to display it to the user when our package installation will be finished.

Here is the description of file entries I made for package.

  • readme.txt - Only mentioned in src attribute, target is empty.
  • CustomLog_Appender.cs.pp - This file is inside Model directory, and target should be the path where you want it to copy. Please make sure to add prefix "content\" to target path, it will be considered as the root directory of the target project.
  • app.config.install.xdt - will be transformed to the target project's app.config file.
  • app.config.uninstall.xdt - will be transformed/removed to the target project's app.config file when you uninstall this package.
  • Install.ps1 - This is the powershell script we need to execute when our package will install. In order to execute this in target project, we have to specify it in target attribute by adding prefix "tools\" to the file name. This is not a physical directory in target project, only purpose of this is if you want to run script you can pleace it in "tools\" directory while specifying target attribute.
  • Uninstall.ps1 - This is the powershell script we need to execute when our package will be uninstalled.

After finishing with Package.nuspec file. Run the following command to generate final package file.

nuget pack Package.nuspec

This will create the package file with nupkg extension in the same directory (project root), values of Id and Version elements from Package.nuspec are concatenated in the resulting file name. In this example, it will create file Log4net.CustomLog_Appender.1.0.0.nupkg.

Points of Interest

The attached download contains only bare minimum content which I mentioned in this article. I created a stand-alone directory with the required files, i.e., readme.txt, CustomLog_Appender.cs.pp, app.config.install.xdt, app.config.uninstall.xdt, Install.ps1, and Uninstall.ps1. You can download this content and copy nuget.exe file in the same directory if you have not set nuget.exe in environment PATH variable, and run the command nuget pack Package.nuspec, it will just create the output package file in the same folder, which you can try installing in any project.

History

  • 4th February, 2019: Initial version

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Muhammad Idrees GS
Software Developer (Senior) Support Services Company Ltd.
Pakistan Pakistan
Karachi - Pakistan.
http://idreesdotnet.blogspot.com/

Comments and Discussions

 
PraiseKeep it up! Pin
kashif.aleem6-Feb-19 23:51
Memberkashif.aleem6-Feb-19 23:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.